Compare commits
2858 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e0747fd50 | ||
|
|
1bcc87c916 | ||
|
|
45797907d3 | ||
|
|
3498dea486 | ||
|
|
a4aab8458b | ||
|
|
534744d615 | ||
|
|
dc5450b95e | ||
|
|
9e0e276740 | ||
|
|
1c9c02f3ce | ||
|
|
7774f9f402 | ||
|
|
5607219945 | ||
|
|
69ef3efd2d | ||
|
|
36c118922b | ||
|
|
1d014953e3 | ||
|
|
c5cb1d9bcc | ||
|
|
86d12cf317 | ||
|
|
db9e59ede1 | ||
|
|
cc9b1b0a05 | ||
|
|
5c29de2013 | ||
|
|
559db104f8 | ||
|
|
365c57be4c | ||
|
|
6f0c84fc68 | ||
|
|
75782d9e99 | ||
|
|
69ddf4936e | ||
|
|
4b3108283f | ||
|
|
12d177a6c8 | ||
|
|
54717dc7e2 | ||
|
|
631c909abd | ||
|
|
208746f28f | ||
|
|
61c5f8ae22 | ||
|
|
4e0cca3a08 | ||
|
|
b86ffc2370 | ||
|
|
33574c9885 | ||
|
|
672c542f99 | ||
|
|
f741fe48a1 | ||
|
|
4347ebcee2 | ||
|
|
4e3e5a5361 | ||
|
|
8e6439c3d1 | ||
|
|
b54d358feb | ||
|
|
78baa7b12d | ||
|
|
19f1dd22e9 | ||
|
|
73f583f68a | ||
|
|
52de43e329 | ||
|
|
49ddacaac0 | ||
|
|
4533b3a1c0 | ||
|
|
f30ea02794 | ||
|
|
7112dda299 | ||
|
|
02636a7108 | ||
|
|
36a9456e0d | ||
|
|
113893dcea | ||
|
|
1abbe9c7b2 | ||
|
|
7205ac791c | ||
|
|
3335b81ad4 | ||
|
|
d9c9438f3c | ||
|
|
37250843e1 | ||
|
|
072ab88721 | ||
|
|
c208e6b107 | ||
|
|
65f3185b39 | ||
|
|
34f73f8a2b | ||
|
|
cb4142a97d | ||
|
|
80d08d5a34 | ||
|
|
052aecf533 | ||
|
|
7bde714f18 | ||
|
|
5ff429f656 | ||
|
|
92f27299e1 | ||
|
|
f9013375f8 | ||
|
|
f18b0045ba | ||
|
|
18173cc4c0 | ||
|
|
6a1ffdd7a5 | ||
|
|
8db78b2190 | ||
|
|
3fcccae241 | ||
|
|
6ff8add164 | ||
|
|
58a8c3c82a | ||
|
|
92c5c656c7 | ||
|
|
61856cd648 | ||
|
|
687f82b5c2 | ||
|
|
920c864bed | ||
|
|
1360c985b7 | ||
|
|
b6f953e13b | ||
|
|
796b96071b | ||
|
|
ec56ccc690 | ||
|
|
95931b67e1 | ||
|
|
706bbb83c9 | ||
|
|
43099f47d0 | ||
|
|
eca86999c3 | ||
|
|
a00b44a5fd | ||
|
|
5b8dcff2a4 | ||
|
|
e4d36e4982 | ||
|
|
adfdbf138d | ||
|
|
8d1ba26492 | ||
|
|
792625f763 | ||
|
|
5e8eae4ef8 | ||
|
|
148212da96 | ||
|
|
23ddb12529 | ||
|
|
186a5e8f7b | ||
|
|
dfb5fe1e04 | ||
|
|
640f75373e | ||
|
|
46e5d96ced | ||
|
|
ad3fbe2052 | ||
|
|
cd6c60fd46 | ||
|
|
f7be386d5a | ||
|
|
89b2131052 | ||
|
|
e5d1ca2e3c | ||
|
|
32b8224afd | ||
|
|
00e7ab1db8 | ||
|
|
e376a53b09 | ||
|
|
17a9cd5666 | ||
|
|
fc3de662c3 | ||
|
|
8de568c2fc | ||
|
|
55661883d9 | ||
|
|
b59d3ebeaa | ||
|
|
2866dcdd03 | ||
|
|
b147f9d58c | ||
|
|
8e2a6470ac | ||
|
|
19230f7672 | ||
|
|
764dfbe46a | ||
|
|
0ec7631310 | ||
|
|
0b39ece990 | ||
|
|
97500dff57 | ||
|
|
dc6299a029 | ||
|
|
58b1e2fb98 | ||
|
|
0302fdaaba | ||
|
|
09b260e0af | ||
|
|
24b7e998b2 | ||
|
|
19068ea5c7 | ||
|
|
b5cd3eb468 | ||
|
|
91a2b5a5f7 | ||
|
|
0096f6d962 | ||
|
|
87cfe41b15 | ||
|
|
c490086097 | ||
|
|
5b3fd7537e | ||
|
|
2cab2583c0 | ||
|
|
a40a3b594d | ||
|
|
0fa586a3d8 | ||
|
|
25977cc569 | ||
|
|
0be7064493 | ||
|
|
0b13dc254a | ||
|
|
c1e953ab6e | ||
|
|
a069764af1 | ||
|
|
3dbe6c3902 | ||
|
|
9f0b380024 | ||
|
|
193b102013 | ||
|
|
511fde91fa | ||
|
|
16e3cfc589 | ||
|
|
ed2012a36c | ||
|
|
46598fde32 | ||
|
|
94d0c5fa5c | ||
|
|
4e72291588 | ||
|
|
74cdf000b4 | ||
|
|
565dc3327b | ||
|
|
462af21793 | ||
|
|
94af659c86 | ||
|
|
b63c9fc67b | ||
|
|
f519b55a47 | ||
|
|
2fb039b429 | ||
|
|
4bce5aed77 | ||
|
|
eca6032306 | ||
|
|
dd2f515af1 | ||
|
|
df25482643 | ||
|
|
df016b3bf9 | ||
|
|
8699e96609 | ||
|
|
b0dcaaf744 | ||
|
|
7db001995c | ||
|
|
c88adf9314 | ||
|
|
b13713ce98 | ||
|
|
66b89d170f | ||
|
|
4311e2e686 | ||
|
|
fd566eff7a | ||
|
|
efaa3f9e55 | ||
|
|
b625ff0902 | ||
|
|
fee80321ef | ||
|
|
4b9331f086 | ||
|
|
df9316b896 | ||
|
|
dae5f86d4e | ||
|
|
75a0cefcb3 | ||
|
|
2271aa5b89 | ||
|
|
b668e2a967 | ||
|
|
ce44c59547 | ||
|
|
84802d931d | ||
|
|
8183c4383e | ||
|
|
af249098cd | ||
|
|
8fdd063f00 | ||
|
|
fe328f6a75 | ||
|
|
ef7163acd6 | ||
|
|
6e45f0d5b8 | ||
|
|
ff41faa3fe | ||
|
|
acb2d332ea | ||
|
|
0fb86f21e4 | ||
|
|
e4991d367e | ||
|
|
3144cfafd5 | ||
|
|
c538067544 | ||
|
|
f8e137a1dc | ||
|
|
36ea38fd91 | ||
|
|
baae5a7215 | ||
|
|
a4b267227e | ||
|
|
58dd159699 | ||
|
|
80082f9d51 | ||
|
|
7975ffc10b | ||
|
|
012083a4a1 | ||
|
|
3c67723e83 | ||
|
|
7f0598b3cb | ||
|
|
c5b27e0ae0 | ||
|
|
4e7ad31f18 | ||
|
|
c7a3e9d4ac | ||
|
|
a05e7a84c4 | ||
|
|
389cb85410 | ||
|
|
81ae2bed25 | ||
|
|
0ab71bc2e7 | ||
|
|
81e6bbe643 | ||
|
|
c4723f3b24 | ||
|
|
df4d8e0411 | ||
|
|
4f1820bcad | ||
|
|
e375097e43 | ||
|
|
c51bec0034 | ||
|
|
081c3fdb5b | ||
|
|
f1e7602775 | ||
|
|
acd75ffe1c | ||
|
|
fc813cb59b | ||
|
|
044d14f542 | ||
|
|
28b9f07b99 | ||
|
|
6f20dd4e45 | ||
|
|
2fc05e5b90 | ||
|
|
9afde31f97 | ||
|
|
e835ce0697 | ||
|
|
6030f56c76 | ||
|
|
ba89b53c63 | ||
|
|
1bfb877a3b | ||
|
|
72776d6fb6 | ||
|
|
7bb93420c4 | ||
|
|
bb1b0e0581 | ||
|
|
45ddcf0df9 | ||
|
|
9c4d7a286f | ||
|
|
7b290b43b5 | ||
|
|
2e2782b030 | ||
|
|
48f23bd1ba | ||
|
|
3ece33b26a | ||
|
|
efa98379b0 | ||
|
|
324aeba70f | ||
|
|
7b6715c7b5 | ||
|
|
1ea392b6c9 | ||
|
|
8b1a3849e6 | ||
|
|
e1d7e98c92 | ||
|
|
04b2e1152c | ||
|
|
d392eba4a0 | ||
|
|
799dec4f1c | ||
|
|
11df6ec927 | ||
|
|
03e363f5d9 | ||
|
|
39211889b0 | ||
|
|
db44549276 | ||
|
|
49e702c0c7 | ||
|
|
c9c5214b3e | ||
|
|
24a537d154 | ||
|
|
c32ecf171b | ||
|
|
a62ae21628 | ||
|
|
12f05e8f72 | ||
|
|
81fb727d20 | ||
|
|
e7655c761d | ||
|
|
464fcf4300 | ||
|
|
73b67ea9df | ||
|
|
04a9c80c14 | ||
|
|
80b5ed3d22 | ||
|
|
be5ad379b6 | ||
|
|
13d901fbfa | ||
|
|
f7e7273e91 | ||
|
|
4b3bd265a6 | ||
|
|
42dcec961b | ||
|
|
6b2a3b2916 | ||
|
|
930f9ca436 | ||
|
|
a96a767b9b | ||
|
|
36627dbe78 | ||
|
|
f86bc31cbb | ||
|
|
4a08ca4b5e | ||
|
|
e857aa0640 | ||
|
|
d3c98ad334 | ||
|
|
00dadfe14d | ||
|
|
f26fa92d47 | ||
|
|
7458072df2 | ||
|
|
89aa2a1902 | ||
|
|
7ab08779dc | ||
|
|
46b686910b | ||
|
|
940a799207 | ||
|
|
778cee3b27 | ||
|
|
fb3252732b | ||
|
|
608f7b75e2 | ||
|
|
83853c3dee | ||
|
|
8a0343ad6b | ||
|
|
92d43ce620 | ||
|
|
6943822fed | ||
|
|
aa146c4125 | ||
|
|
b8ec6babb5 | ||
|
|
505f4a2b4f | ||
|
|
1bb0d017c3 | ||
|
|
f33ce615d9 | ||
|
|
0edb49d7c6 | ||
|
|
1fa5a03605 | ||
|
|
fc6bda5cd8 | ||
|
|
59589b6a71 | ||
|
|
57b0624a0a | ||
|
|
2f22395eea | ||
|
|
21bc8d4dfb | ||
|
|
2e74fd8196 | ||
|
|
fee48ab04c | ||
|
|
591f885e67 | ||
|
|
ef803016c0 | ||
|
|
bac51045e5 | ||
|
|
cb5e9393a4 | ||
|
|
097509dd2a | ||
|
|
6cd44248c7 | ||
|
|
1fd61fbcef | ||
|
|
cd2245ef03 | ||
|
|
5ddc746915 | ||
|
|
f7d67067ad | ||
|
|
d1a824f76e | ||
|
|
d7b0fc09d7 | ||
|
|
2d07a6ec1f | ||
|
|
014ba5a7aa | ||
|
|
2f12e524dd | ||
|
|
ea6af75e8e | ||
|
|
a595c6ec6f | ||
|
|
98f785d199 | ||
|
|
a0ed0c864f | ||
|
|
d4ac42749e | ||
|
|
86414671d6 | ||
|
|
6cb935982d | ||
|
|
39ad040d8a | ||
|
|
4cc3824653 | ||
|
|
6d3e0600a1 | ||
|
|
b52c9c1676 | ||
|
|
f7506251fe | ||
|
|
5b06f298f1 | ||
|
|
dc5488a198 | ||
|
|
23d162e9e4 | ||
|
|
de6b660b12 | ||
|
|
60c175a51d | ||
|
|
c0a39c8064 | ||
|
|
61f4f93cc6 | ||
|
|
764404a472 | ||
|
|
04da85c88a | ||
|
|
9551b7a597 | ||
|
|
f1d537d035 | ||
|
|
b43e723c84 | ||
|
|
5466039c1a | ||
|
|
74e11117be | ||
|
|
e0abab2dc4 | ||
|
|
08ff3ff55c | ||
|
|
0b79d99cac | ||
|
|
38f655e7b8 | ||
|
|
6f546ffe95 | ||
|
|
ef8211a46e | ||
|
|
52ea5e4074 | ||
|
|
99621cf08a | ||
|
|
a9ae9ce758 | ||
|
|
fc5a529df8 | ||
|
|
bc1081550d | ||
|
|
e311ab1605 | ||
|
|
994ade0638 | ||
|
|
cf9522bd3b | ||
|
|
7fd1858db0 | ||
|
|
c41509151b | ||
|
|
4e12853d4a | ||
|
|
e1fadbc9b9 | ||
|
|
3c4d13d8b9 | ||
|
|
732159db07 | ||
|
|
00894eaccf | ||
|
|
17818178cf | ||
|
|
89c0abfada | ||
|
|
8bbdd92739 | ||
|
|
f724ceb052 | ||
|
|
b7ac85005f | ||
|
|
8ec899a8e7 | ||
|
|
1013c6a50e | ||
|
|
2cd5d3bab3 | ||
|
|
d9494960a2 | ||
|
|
c0eb6590da | ||
|
|
a9cefcb823 | ||
|
|
d1ff491010 | ||
|
|
2aa34f05d3 | ||
|
|
135d6915ee | ||
|
|
6282195b80 | ||
|
|
adec4f08c6 | ||
|
|
b24fe2f122 | ||
|
|
7b35bcb68a | ||
|
|
4b629045c3 | ||
|
|
c6ba1d05d1 | ||
|
|
3adac90573 | ||
|
|
0332188b6b | ||
|
|
790a1e2812 | ||
|
|
3bd1e13321 | ||
|
|
0cd642ca11 | ||
|
|
d5995998b0 | ||
|
|
62babb9c22 | ||
|
|
b98c69521b | ||
|
|
6cd37b3809 | ||
|
|
238170d0e2 | ||
|
|
6fb5b25530 | ||
|
|
d63f9e1eff | ||
|
|
461865bf3b | ||
|
|
e95d9b9cf6 | ||
|
|
c52b2a4fa9 | ||
|
|
d0ec5d2da0 | ||
|
|
6134058abc | ||
|
|
bd4121c260 | ||
|
|
fbab047d7f | ||
|
|
e216144d07 | ||
|
|
94d5b11191 | ||
|
|
d3c89da2eb | ||
|
|
754a8a637e | ||
|
|
ab24a50de7 | ||
|
|
8099dde34b | ||
|
|
99198fb07f | ||
|
|
18d02a0c95 | ||
|
|
e6cac7d5fb | ||
|
|
d8d6f00fee | ||
|
|
ef5c1a1099 | ||
|
|
2d92d8ee67 | ||
|
|
ae60266477 | ||
|
|
88da5261fc | ||
|
|
15efe9f702 | ||
|
|
badbc5c66e | ||
|
|
d6eea41e13 | ||
|
|
6a2c54adf5 | ||
|
|
75d4bc7c18 | ||
|
|
834f7feb27 | ||
|
|
5d2af3f9e1 | ||
|
|
60e8e4a82a | ||
|
|
92e23476ab | ||
|
|
4560fef058 | ||
|
|
c5d6574617 | ||
|
|
de61a0ca45 | ||
|
|
98470caae1 | ||
|
|
53486c067b | ||
|
|
5b805852fa | ||
|
|
94d02eeb4e | ||
|
|
dbfec1b247 | ||
|
|
a9a1a71145 | ||
|
|
91537bf5f3 | ||
|
|
1baad08288 | ||
|
|
1c5b846488 | ||
|
|
6867760cc4 | ||
|
|
516a9209b7 | ||
|
|
e352c953dc | ||
|
|
f455846d18 | ||
|
|
6d45ac942d | ||
|
|
48939abafa | ||
|
|
fbc9595f12 | ||
|
|
ed2c714419 | ||
|
|
ef7731fb8b | ||
|
|
b5ec03f585 | ||
|
|
a8b224a364 | ||
|
|
3f3943a233 | ||
|
|
4171b4ae47 | ||
|
|
baa4c44632 | ||
|
|
ffed8f37de | ||
|
|
8978a0d159 | ||
|
|
4d09be0cd3 | ||
|
|
987be01bd6 | ||
|
|
7f59fc6321 | ||
|
|
feeb122c0d | ||
|
|
0754f108c9 | ||
|
|
ea82f86749 | ||
|
|
7bd217386a | ||
|
|
1f1e02fee1 | ||
|
|
6d1e4fb3da | ||
|
|
9090f84611 | ||
|
|
8e5ddba173 | ||
|
|
86fccb5071 | ||
|
|
414c3baf15 | ||
|
|
bbbddbf617 | ||
|
|
7638b5a95e | ||
|
|
3df6b05c00 | ||
|
|
8566c58f2b | ||
|
|
33ccb71526 | ||
|
|
2b07b92039 | ||
|
|
959bc269e4 | ||
|
|
8609068dbf | ||
|
|
d6cea80768 | ||
|
|
0f2d6fb324 | ||
|
|
f5d303045c | ||
|
|
ad61b0c84e | ||
|
|
18373bb679 | ||
|
|
c30dfe3bfe | ||
|
|
4993961c16 | ||
|
|
89aece7c28 | ||
|
|
ec96946513 | ||
|
|
e49aa0d424 | ||
|
|
b5c929572a | ||
|
|
394822aa50 | ||
|
|
d12ba5e527 | ||
|
|
ab316bcb2e | ||
|
|
93fc08df45 | ||
|
|
266dd02ef5 | ||
|
|
61c1e359e9 | ||
|
|
32c83986e5 | ||
|
|
f64af2e69d | ||
|
|
6e925c7cdd | ||
|
|
5f50f776c6 | ||
|
|
946ccf3db4 | ||
|
|
7a0aea390b | ||
|
|
ff0e6315f9 | ||
|
|
b7fb485349 | ||
|
|
45cbe66d5d | ||
|
|
bc7a6399c1 | ||
|
|
2d5a3f24aa | ||
|
|
1639429edd | ||
|
|
f81abf4883 | ||
|
|
dcddb7d50e | ||
|
|
42a763031c | ||
|
|
0964ca5918 | ||
|
|
4e870b83cd | ||
|
|
0b19c9be0b | ||
|
|
ac655902a0 | ||
|
|
e4fb293b55 | ||
|
|
d3df64ef4b | ||
|
|
93e3d14f34 | ||
|
|
bc687d412d | ||
|
|
71e85b44b0 | ||
|
|
d4b59aff97 | ||
|
|
e3c21e1f3d | ||
|
|
e012250e0a | ||
|
|
c6bf82664c | ||
|
|
c61e98009f | ||
|
|
44bccaaad6 | ||
|
|
aebca14dea | ||
|
|
4576f27f6e | ||
|
|
0c213fca40 | ||
|
|
891f596191 | ||
|
|
eb2b724902 | ||
|
|
1f8c896181 | ||
|
|
12b39e1fbe | ||
|
|
206d31f27a | ||
|
|
6709331182 | ||
|
|
9460e04ea5 | ||
|
|
462bfea733 | ||
|
|
a2d61dfaeb | ||
|
|
538425e35c | ||
|
|
2e86c1f1a2 | ||
|
|
8aa4d46ce0 | ||
|
|
cdf67a1421 | ||
|
|
93d905d93d | ||
|
|
2c35203647 | ||
|
|
216871fe56 | ||
|
|
03511f39b1 | ||
|
|
5ad0832b22 | ||
|
|
d49e7ba4b2 | ||
|
|
a621b36389 | ||
|
|
d093df02ac | ||
|
|
2e1529e78d | ||
|
|
90be1db402 | ||
|
|
29265e4181 | ||
|
|
2689a7ef13 | ||
|
|
88b58cf23d | ||
|
|
e2e433480b | ||
|
|
b8bc3e4e94 | ||
|
|
c28f52b816 | ||
|
|
ca34309f75 | ||
|
|
ea614a0f3f | ||
|
|
3e7dc7af29 | ||
|
|
0b3e3fd19a | ||
|
|
d3372dc05c | ||
|
|
7d141cb183 | ||
|
|
755699c31b | ||
|
|
acf3d4cd74 | ||
|
|
92414c91b5 | ||
|
|
ff67f402bb | ||
|
|
f048a3f1d8 | ||
|
|
9d39cb51cf | ||
|
|
7624a9d34c | ||
|
|
0fe73fb6ed | ||
|
|
ae11652493 | ||
|
|
0ab46870ae | ||
|
|
a5381374b8 | ||
|
|
7fae655651 | ||
|
|
3d9959c8e5 | ||
|
|
bc86576aa8 | ||
|
|
7c2bce8baf | ||
|
|
b062bbf58d | ||
|
|
8e5a048913 | ||
|
|
ff642ffff7 | ||
|
|
656c12cebd | ||
|
|
48f3fcf3bd | ||
|
|
d797af695b | ||
|
|
75e0161c5f | ||
|
|
c0662816f1 | ||
|
|
415a31ce23 | ||
|
|
6e603e2e51 | ||
|
|
9af318a767 | ||
|
|
30827120f6 | ||
|
|
6194e12616 | ||
|
|
59a27fb652 | ||
|
|
f8ec57c2f6 | ||
|
|
844ef5e232 | ||
|
|
5bf3f7b77e | ||
|
|
00c0afd093 | ||
|
|
5228739c87 | ||
|
|
0f6d1268c7 | ||
|
|
b2a4515d2b | ||
|
|
3576c80d33 | ||
|
|
77afd2d36c | ||
|
|
e6ddd5d9c2 | ||
|
|
b9bf1a234e | ||
|
|
0ce9122449 | ||
|
|
dc41657872 | ||
|
|
64d9380031 | ||
|
|
eca7484e30 | ||
|
|
2b5c393a13 | ||
|
|
0817d416a3 | ||
|
|
ccff753305 | ||
|
|
11481a430a | ||
|
|
e252b22735 | ||
|
|
a32ca8eb1d | ||
|
|
1979a2279f | ||
|
|
1434f942dd | ||
|
|
955d325f07 | ||
|
|
3fac3fe2d7 | ||
|
|
6ff217e74e | ||
|
|
cd667fe34b | ||
|
|
3d6202ecfd | ||
|
|
c40f0ef2bb | ||
|
|
c4b169edd1 | ||
|
|
c6bba98828 | ||
|
|
e5d4f1bb58 | ||
|
|
cf522d60ca | ||
|
|
daf72a7c61 | ||
|
|
f116ad0594 | ||
|
|
e59d40834d | ||
|
|
d06328af7f | ||
|
|
baa9d7d836 | ||
|
|
b030df55ee | ||
|
|
64eb8a2d0d | ||
|
|
7d7c36e0c7 | ||
|
|
9ad5768050 | ||
|
|
1cafbcff38 | ||
|
|
4bbf93fd0c | ||
|
|
120918bc37 | ||
|
|
253448cffb | ||
|
|
26f19fb90e | ||
|
|
e8f982e909 | ||
|
|
0fa433187e | ||
|
|
b0069299ea | ||
|
|
375d5d3e9b | ||
|
|
2d9398ae1a | ||
|
|
fc69e91636 | ||
|
|
5c56cbd672 | ||
|
|
a42255158d | ||
|
|
f3d10a97df | ||
|
|
5d15a3d69f | ||
|
|
94292872dc | ||
|
|
5c8a1e7d10 | ||
|
|
e98d8426bb | ||
|
|
77f80cecf9 | ||
|
|
bce26a1499 | ||
|
|
453f1c6edc | ||
|
|
9f8a36f2c9 | ||
|
|
e6a5945463 | ||
|
|
60393fbc5f | ||
|
|
b9a925dbd4 | ||
|
|
5dbdb1bcb5 | ||
|
|
d55cefc086 | ||
|
|
5380e6d98b | ||
|
|
45cc24e0b8 | ||
|
|
76bf1791eb | ||
|
|
7e9a4c4b16 | ||
|
|
75cd5f169f | ||
|
|
10636c82a2 | ||
|
|
04a6729963 | ||
|
|
b5617398ad | ||
|
|
da14641ccb | ||
|
|
3997bd2396 | ||
|
|
c876350e05 | ||
|
|
1e07b981f0 | ||
|
|
756e909f5e | ||
|
|
2404392daf | ||
|
|
bf6576f5af | ||
|
|
158c2ab76e | ||
|
|
56fa50385e | ||
|
|
eb05ab5fab | ||
|
|
7f87b637d5 | ||
|
|
4b213adc75 | ||
|
|
7e7d5bbf17 | ||
|
|
e18c0a0c19 | ||
|
|
02b6ffc771 | ||
|
|
340d66c24c | ||
|
|
2cbf9d3620 | ||
|
|
11e6793544 | ||
|
|
d07e0336a2 | ||
|
|
7dbfb3d9ae | ||
|
|
02ef5db6e3 | ||
|
|
bb43bb5508 | ||
|
|
a771f083a7 | ||
|
|
67f8bce1dd | ||
|
|
3b6dd23598 | ||
|
|
66bbeae7da | ||
|
|
059334a861 | ||
|
|
f874c8309f | ||
|
|
27be812cc3 | ||
|
|
2adc2e97e1 | ||
|
|
dc4279a2d0 | ||
|
|
85bff4525a | ||
|
|
bd648c0745 | ||
|
|
d5e7005edd | ||
|
|
8913c7ea18 | ||
|
|
0e2afe4f98 | ||
|
|
a936f7cdbe | ||
|
|
44fa466618 | ||
|
|
b05d84e8e3 | ||
|
|
7787c1ddd7 | ||
|
|
52bfddd2e7 | ||
|
|
f59adcdc37 | ||
|
|
e996d3398f | ||
|
|
d73892d25a | ||
|
|
1ebf614d79 | ||
|
|
f9c995f03f | ||
|
|
2dcdd561f0 | ||
|
|
90f97aa8d7 | ||
|
|
5a4b067cee | ||
|
|
cabcc761c6 | ||
|
|
a40ac1c46c | ||
|
|
85ddfc4d21 | ||
|
|
037a35df1d | ||
|
|
64c753023e | ||
|
|
9d72ffb9fe | ||
|
|
4162d4fbc6 | ||
|
|
a4d16bffd3 | ||
|
|
bcaf1489c0 | ||
|
|
a85712e8aa | ||
|
|
0f95d02cdc | ||
|
|
c151d55237 | ||
|
|
785cd6bc11 | ||
|
|
8ec7e3cc97 | ||
|
|
e376493970 | ||
|
|
08e2322d79 | ||
|
|
2832767daa | ||
|
|
62e12ee0aa | ||
|
|
80e73c1089 | ||
|
|
4f93995bd7 | ||
|
|
03c7dac92f | ||
|
|
d0b93f565a | ||
|
|
1512fbae55 | ||
|
|
4d91afdf9f | ||
|
|
c239c3fea3 | ||
|
|
a0b431e8c4 | ||
|
|
005e03aeb3 | ||
|
|
6002d373e2 | ||
|
|
a53066424b | ||
|
|
38ab02ff88 | ||
|
|
fee0b29a0b | ||
|
|
2553695029 | ||
|
|
301939983f | ||
|
|
cd9ae1fbad | ||
|
|
54e9bc86cc | ||
|
|
a1dd4c28c3 | ||
|
|
6d3857f337 | ||
|
|
c4be3e731a | ||
|
|
ea4407b847 | ||
|
|
a62a83044f | ||
|
|
e7e7b06744 | ||
|
|
2222e31885 | ||
|
|
c3f7f83c55 | ||
|
|
86e18f68c4 | ||
|
|
cc7d9e03d9 | ||
|
|
c8639ae434 | ||
|
|
85cf32516e | ||
|
|
bc85817b07 | ||
|
|
1016394be5 | ||
|
|
de35cbf4af | ||
|
|
bb1acee36a | ||
|
|
c6a2ed78c3 | ||
|
|
7729958082 | ||
|
|
ccdaec86b3 | ||
|
|
42afeef993 | ||
|
|
3d06911886 | ||
|
|
98f929e41e | ||
|
|
94d4e0231e | ||
|
|
e60cdb2e3b | ||
|
|
550c021097 | ||
|
|
1aec7f71b8 | ||
|
|
07565927f2 | ||
|
|
fda78ede1b | ||
|
|
1249a4f8d3 | ||
|
|
4e631f1536 | ||
|
|
1757dbc225 | ||
|
|
8149e204d2 | ||
|
|
412f2ef63e | ||
|
|
ea33673daa | ||
|
|
169b816613 | ||
|
|
48edf077fc | ||
|
|
91db153710 | ||
|
|
ea1f010b1a | ||
|
|
935a852745 | ||
|
|
0eb6d7de3c | ||
|
|
3118cd4fa5 | ||
|
|
18832fcb37 | ||
|
|
612554f6af | ||
|
|
1c794591b9 | ||
|
|
a8d95b284d | ||
|
|
772c2dba2f | ||
|
|
9d1a210c97 | ||
|
|
b04b966db1 | ||
|
|
e2c61d90e9 | ||
|
|
d50655c4f7 | ||
|
|
5e4c63736b | ||
|
|
b90b71c8c0 | ||
|
|
722033e906 | ||
|
|
b6ec91fcd0 | ||
|
|
e94c0d003a | ||
|
|
5c46ccb37e | ||
|
|
fd989feea3 | ||
|
|
1b53e83dde | ||
|
|
3ebe827b51 | ||
|
|
b8266e1169 | ||
|
|
4efbe24607 | ||
|
|
e96ac1f6ff | ||
|
|
14915368c7 | ||
|
|
0ff5c18743 | ||
|
|
240814cc85 | ||
|
|
1912350cc6 | ||
|
|
146faa6008 | ||
|
|
e5fa67850b | ||
|
|
1377b51341 | ||
|
|
db67fb3539 | ||
|
|
6d216406f3 | ||
|
|
ab225eaec1 | ||
|
|
4d6d9e567d | ||
|
|
0fcf0142ba | ||
|
|
ab907e4fef | ||
|
|
1c1c3eb271 | ||
|
|
a03a569534 | ||
|
|
309fb0fa83 | ||
|
|
14c1431848 | ||
|
|
c3dc32415b | ||
|
|
44d6246278 | ||
|
|
5bbd7e03f9 | ||
|
|
f5f463b411 | ||
|
|
6eaefbe25c | ||
|
|
4b5c2c85a5 | ||
|
|
0f951cd322 | ||
|
|
cf0da32fd0 | ||
|
|
5a3085c42b | ||
|
|
42cc480bf5 | ||
|
|
2822eda858 | ||
|
|
6c804b5ca2 | ||
|
|
5149d1395e | ||
|
|
f75a2dab8d | ||
|
|
a045c88c61 | ||
|
|
a5fdf917df | ||
|
|
80a0e47f1a | ||
|
|
a304313ad4 | ||
|
|
05b1d5a4da | ||
|
|
edeef3541c | ||
|
|
cd929d8e65 | ||
|
|
b005971427 | ||
|
|
c35614fb63 | ||
|
|
18e16b09f2 | ||
|
|
b18b76c1f0 | ||
|
|
c9f65267ba | ||
|
|
cce52c673b | ||
|
|
fdde216f0e | ||
|
|
d1901a97d1 | ||
|
|
eb2077b95b | ||
|
|
fad3bdeed4 | ||
|
|
e1a537e547 | ||
|
|
ba098e1199 | ||
|
|
0ec755fccf | ||
|
|
997dd433b3 | ||
|
|
4aea1567db | ||
|
|
446c8df3b4 | ||
|
|
f4bd868d6a | ||
|
|
8490264af3 | ||
|
|
505cfdd193 | ||
|
|
39ecd1545c | ||
|
|
9b80c0028d | ||
|
|
35276b55af | ||
|
|
eb63dd6227 | ||
|
|
9f24b3c584 | ||
|
|
53e951d96d | ||
|
|
17dd386937 | ||
|
|
3eb00382e7 | ||
|
|
98b5f7a26a | ||
|
|
fb70ec1cb0 | ||
|
|
47ada87ba2 | ||
|
|
7a949dd328 | ||
|
|
3982217b67 | ||
|
|
8cb61da9c2 | ||
|
|
375c780d3b | ||
|
|
f3b6c2ce5d | ||
|
|
a7a2c96382 | ||
|
|
99336ef746 | ||
|
|
72d2adfe1c | ||
|
|
707574e294 | ||
|
|
18ffbd0027 | ||
|
|
68a0e4f39a | ||
|
|
ddb99ced34 | ||
|
|
cb2782bc71 | ||
|
|
52cade9267 | ||
|
|
ec600eb2f4 | ||
|
|
61e96e2dab | ||
|
|
9f5433053d | ||
|
|
d2489a57b7 | ||
|
|
e5841cd467 | ||
|
|
820d52428f | ||
|
|
ae6931b046 | ||
|
|
90a97b90bb | ||
|
|
4b38d81f00 | ||
|
|
8c27ca9e36 | ||
|
|
4b89fc390d | ||
|
|
29c1766714 | ||
|
|
2e5a9f117c | ||
|
|
2abcbcd30f | ||
|
|
ac8bde4ea8 | ||
|
|
78a13efc6c | ||
|
|
c99e859978 | ||
|
|
ed448cb99f | ||
|
|
5d472a1f2e | ||
|
|
92a938db0e | ||
|
|
aa979b2046 | ||
|
|
2daaf899d3 | ||
|
|
fa4c6bccbc | ||
|
|
bedea4b11a | ||
|
|
bc415242f2 | ||
|
|
2eeb7552d6 | ||
|
|
3a6ecb10a7 | ||
|
|
f55004d1e4 | ||
|
|
1a3304a886 | ||
|
|
58bcbe75b7 | ||
|
|
753df6decf | ||
|
|
c32f947c18 | ||
|
|
6123e4ac8a | ||
|
|
c52f16f5b8 | ||
|
|
3dac65a012 | ||
|
|
bc089bc4c1 | ||
|
|
1075ded4dc | ||
|
|
5578ea2f30 | ||
|
|
ade3e58829 | ||
|
|
ee2e88476c | ||
|
|
1726e983ad | ||
|
|
44341f0bcc | ||
|
|
366da3d0fe | ||
|
|
f7fa2f605b | ||
|
|
6854dea47e | ||
|
|
9d7e1cd99d | ||
|
|
330f3dff77 | ||
|
|
073c46e7bf | ||
|
|
9950b2ea34 | ||
|
|
a7c2a5c49b | ||
|
|
c2c39cd0d0 | ||
|
|
b6e7c36b11 | ||
|
|
002f92fe1e | ||
|
|
d6d0cdf9aa | ||
|
|
7d002df94f | ||
|
|
f0d7408f94 | ||
|
|
7154678347 | ||
|
|
68f0743c40 | ||
|
|
3d5905c878 | ||
|
|
a344a1ca59 | ||
|
|
5f539ddfb7 | ||
|
|
a974235d10 | ||
|
|
2d17ef5c7e | ||
|
|
dda8c209df | ||
|
|
be88d5dfb8 | ||
|
|
4a537d82d3 | ||
|
|
6f36723097 | ||
|
|
35bce924f8 | ||
|
|
9356e8ccb5 | ||
|
|
1ffde24ef6 | ||
|
|
2f52bef882 | ||
|
|
b1369582e1 | ||
|
|
cf176719a8 | ||
|
|
d7ed72ac06 | ||
|
|
83322664f1 | ||
|
|
5af7e96f70 | ||
|
|
c95041d7c3 | ||
|
|
62f40dcb21 | ||
|
|
b4f88e303a | ||
|
|
cd0ec69446 | ||
|
|
909415d167 | ||
|
|
337bc6d97a | ||
|
|
6c9fab7da1 | ||
|
|
315b1f5913 | ||
|
|
c2d1859858 | ||
|
|
37ba7316e0 | ||
|
|
fc1292b1d1 | ||
|
|
0507680cc1 | ||
|
|
60475d3354 | ||
|
|
b1cd794b2c | ||
|
|
cd98ecff88 | ||
|
|
f8ec4027ee | ||
|
|
d980a5cf44 | ||
|
|
761604d044 | ||
|
|
0f4161f616 | ||
|
|
4a668c97a3 | ||
|
|
9d687c25b3 | ||
|
|
0b73082aa0 | ||
|
|
085e1ae157 | ||
|
|
7c0db3203b | ||
|
|
bc987ed03f | ||
|
|
57e1bf3afd | ||
|
|
90606bd410 | ||
|
|
9744a3745c | ||
|
|
14e051f970 | ||
|
|
2396c9b6da | ||
|
|
c386a9d140 | ||
|
|
12847593c2 | ||
|
|
6cfa024ea2 | ||
|
|
4b43d99660 | ||
|
|
c1bfc29e7f | ||
|
|
7e91e27f14 | ||
|
|
f8015036a9 | ||
|
|
b7ef1607dc | ||
|
|
b93a31dd5b | ||
|
|
fa09964f10 | ||
|
|
0038728be3 | ||
|
|
ec56651959 | ||
|
|
8259371d6a | ||
|
|
d8623991c0 | ||
|
|
0c205431b0 | ||
|
|
58dbaf4a51 | ||
|
|
3b50ebae83 | ||
|
|
8b895eff7a | ||
|
|
70af0365bf | ||
|
|
bb3e3bfa1f | ||
|
|
54aa98a752 | ||
|
|
3bff0aa1ea | ||
|
|
b012231635 | ||
|
|
6c6f05fc91 | ||
|
|
3338d5e105 | ||
|
|
390ec2f0c1 | ||
|
|
4ebdf7a6e8 | ||
|
|
6f1366dbb5 | ||
|
|
503791d3c0 | ||
|
|
902dc49de2 | ||
|
|
2d299f31e5 | ||
|
|
df298587b8 | ||
|
|
b9c33008d8 | ||
|
|
ca97f1c4a3 | ||
|
|
effed56653 | ||
|
|
7b501aa89b | ||
|
|
6c0d0d465b | ||
|
|
a52bd20912 | ||
|
|
aabcc445ee | ||
|
|
d491e504cc | ||
|
|
3142e430e7 | ||
|
|
b784d80a50 | ||
|
|
7344654308 | ||
|
|
e01b35c39c | ||
|
|
b8b2c4ee73 | ||
|
|
8cd1ce97f7 | ||
|
|
05b514b743 | ||
|
|
22596e0f5b | ||
|
|
e7031794df | ||
|
|
af69a0a1fd | ||
|
|
52fa690764 | ||
|
|
4ad411e320 | ||
|
|
09da190e93 | ||
|
|
797dd56bb7 | ||
|
|
5e1d77fcd5 | ||
|
|
2820f2658b | ||
|
|
f3fc32660b | ||
|
|
2d4b00ec24 | ||
|
|
86808f5035 | ||
|
|
82946e2372 | ||
|
|
22d117a397 | ||
|
|
662cfc9fdc | ||
|
|
a3e73909cf | ||
|
|
95bafc8ced | ||
|
|
05f1906f77 | ||
|
|
dbbb131424 | ||
|
|
fc7ec35242 | ||
|
|
7ac10472dc | ||
|
|
d255889c5c | ||
|
|
7662da22fc | ||
|
|
64cc200df0 | ||
|
|
63c983683c | ||
|
|
92f79c99f7 | ||
|
|
24f937fe4f | ||
|
|
47bb682890 | ||
|
|
9ab674ef9a | ||
|
|
83eb0de916 | ||
|
|
6359a12dbb | ||
|
|
eb8e96f413 | ||
|
|
138bd96167 | ||
|
|
0d5ea4711d | ||
|
|
fa4212f07b | ||
|
|
c7635a7e3d | ||
|
|
3923c4cfd2 | ||
|
|
6ad884080c | ||
|
|
c578a43611 | ||
|
|
aebfa7161f | ||
|
|
682bc502e2 | ||
|
|
cf018ff74c | ||
|
|
5a9cb251ad | ||
|
|
bbbf5a5edf | ||
|
|
7df0449100 | ||
|
|
820add87ea | ||
|
|
9a574aa00c | ||
|
|
10da82a331 | ||
|
|
26c816e78c | ||
|
|
808e238384 | ||
|
|
ff068e032b | ||
|
|
5072e96277 | ||
|
|
c55c1c0615 | ||
|
|
9983faef70 | ||
|
|
70a264e939 | ||
|
|
9ef6008c67 | ||
|
|
5a3e816544 | ||
|
|
3e385fb884 | ||
|
|
fa5cce7477 | ||
|
|
b2de76e8a0 | ||
|
|
edb363b236 | ||
|
|
12dcfdb5e1 | ||
|
|
86c1dd7f32 | ||
|
|
d8ff4285da | ||
|
|
7effde0478 | ||
|
|
559ee931fb | ||
|
|
cb27a7fe3e | ||
|
|
c33a8dc2c8 | ||
|
|
11fcf88b88 | ||
|
|
4f0baa80d7 | ||
|
|
ddea97bc88 | ||
|
|
102c9891a0 | ||
|
|
fb4784b695 | ||
|
|
9a0650c703 | ||
|
|
a62b93acec | ||
|
|
2d11bdc736 | ||
|
|
7d263d36a0 | ||
|
|
4965fd5888 | ||
|
|
36db8b6f59 | ||
|
|
9e69f62a39 | ||
|
|
f307021cf1 | ||
|
|
0709c402f8 | ||
|
|
df5deb8b88 | ||
|
|
111dd40097 | ||
|
|
c83e7d7995 | ||
|
|
718f964de7 | ||
|
|
ce0db9b968 | ||
|
|
3f0d602353 | ||
|
|
5364d778b0 | ||
|
|
0ddec227fa | ||
|
|
a5ea35ee56 | ||
|
|
a3e03fb889 | ||
|
|
b7711f0182 | ||
|
|
aae199ff1c | ||
|
|
8d0611a188 | ||
|
|
958b245c3d | ||
|
|
fbb77092eb | ||
|
|
ebc8328712 | ||
|
|
7967c6dfc8 | ||
|
|
fca08a5162 | ||
|
|
49f65d6762 | ||
|
|
7684e1bad5 | ||
|
|
46e974d201 | ||
|
|
ab55d0abc8 | ||
|
|
ed668d5144 | ||
|
|
dec0f28a2b | ||
|
|
de7d79f1f4 | ||
|
|
d204d580dd | ||
|
|
e4e38edf24 | ||
|
|
3721a1f90b | ||
|
|
897850c3eb | ||
|
|
a22efb25c6 | ||
|
|
f7e80879ae | ||
|
|
bf902e6398 | ||
|
|
aed817f724 | ||
|
|
5ac76d29fc | ||
|
|
81311ddd03 | ||
|
|
3c52070243 | ||
|
|
9070f50658 | ||
|
|
ecc3c56cb1 | ||
|
|
a86191f6e0 | ||
|
|
aa5fdac522 | ||
|
|
3cd32c74a1 | ||
|
|
2e2f6a03d9 | ||
|
|
25a073f20b | ||
|
|
0fb244e381 | ||
|
|
631ba70b72 | ||
|
|
0a1d6b2a6f | ||
|
|
94bfa5ea25 | ||
|
|
118c14bf1e | ||
|
|
cf395f707a | ||
|
|
6e040fa87c | ||
|
|
c0a7f84382 | ||
|
|
826d7b7c26 | ||
|
|
91e63d0f19 | ||
|
|
2d69e5f6ff | ||
|
|
23f0fcd024 | ||
|
|
3fcd82abad | ||
|
|
d766c39791 | ||
|
|
a76c3b3baa | ||
|
|
9290b4eecc | ||
|
|
eccfec4b4f | ||
|
|
80aadd3874 | ||
|
|
9da15826db | ||
|
|
8c403f8795 | ||
|
|
b010824dbd | ||
|
|
3340f8864f | ||
|
|
9fb3cdfc4c | ||
|
|
3a3a2c2702 | ||
|
|
be38919758 | ||
|
|
a816ccf6ee | ||
|
|
5e85068e35 | ||
|
|
77865ab4a1 | ||
|
|
5fc6ee27f6 | ||
|
|
5995adf8b6 | ||
|
|
c9f47ed89a | ||
|
|
d6325ffcfa | ||
|
|
9d72472114 | ||
|
|
102350a125 | ||
|
|
1c650cb136 | ||
|
|
625e74653d | ||
|
|
5bad762293 | ||
|
|
fc1ff11352 | ||
|
|
0595ba384e | ||
|
|
f80f5e9955 | ||
|
|
f689014c3e | ||
|
|
9b24655bbf | ||
|
|
c3201070a1 | ||
|
|
8a146209e4 | ||
|
|
2b719726cd | ||
|
|
27209e4836 | ||
|
|
0497762f94 | ||
|
|
b38f1e744b | ||
|
|
481c49e18e | ||
|
|
69cf9115b5 | ||
|
|
3eedd89efe | ||
|
|
ad262ea624 | ||
|
|
08cd8c95f7 | ||
|
|
c86790c6b8 | ||
|
|
e466d9252f | ||
|
|
02f8a0baa6 | ||
|
|
9f7bbe3f9d | ||
|
|
e81e0e97e0 | ||
|
|
52554c3972 | ||
|
|
48b6c4876a | ||
|
|
99e89c2540 | ||
|
|
0e8453d4ed | ||
|
|
c750b746b3 | ||
|
|
0ac07e2e83 | ||
|
|
ead6cb9ca3 | ||
|
|
c401f9211d | ||
|
|
abb72c0a9a | ||
|
|
bc9172bd43 | ||
|
|
b205a5e49d | ||
|
|
fccf9642d8 | ||
|
|
96dd68f52b | ||
|
|
7f923bb389 | ||
|
|
7ab7ebb3f6 | ||
|
|
4eacb58a3e | ||
|
|
24248832d1 | ||
|
|
a53f0900bd | ||
|
|
6a1a5a0f74 | ||
|
|
65aa4e145e | ||
|
|
cf903aa082 | ||
|
|
75dd86e8ef | ||
|
|
70a3d62c14 | ||
|
|
291e3cd229 | ||
|
|
9b28c1fdd2 | ||
|
|
d1d73da039 | ||
|
|
59f041b9a9 | ||
|
|
fbf3b9ce4d | ||
|
|
9be2e1c4c9 | ||
|
|
a06c9f890c | ||
|
|
fc8e8874a9 | ||
|
|
09e0f68616 | ||
|
|
0e7e36f80d | ||
|
|
8484dcd332 | ||
|
|
8a46296b48 | ||
|
|
b748a910b2 | ||
|
|
932603eea3 | ||
|
|
5056972f79 | ||
|
|
d99ccca291 | ||
|
|
1d9b26d9ec | ||
|
|
3a64707c6e | ||
|
|
6f4071bd75 | ||
|
|
1354fdc613 | ||
|
|
991244655a | ||
|
|
0d07d935bf | ||
|
|
bd56b642a7 | ||
|
|
d989f0edf2 | ||
|
|
f561cf9c8f | ||
|
|
13502e5467 | ||
|
|
045c2a6f05 | ||
|
|
725e9feb68 | ||
|
|
6e45126f53 | ||
|
|
6ebc39e038 | ||
|
|
8b0a7c302b | ||
|
|
0c3e50da2f | ||
|
|
d220cf4acc | ||
|
|
eb1ce7e927 | ||
|
|
46f6f41a6b | ||
|
|
2fa59fc43c | ||
|
|
b7763b3887 | ||
|
|
8c002b67d5 | ||
|
|
7692c4a377 | ||
|
|
1f0acec06c | ||
|
|
1b5295a8fe | ||
|
|
0b699a3f20 | ||
|
|
409d7a499d | ||
|
|
ba7428f3d6 | ||
|
|
c12c32d26e | ||
|
|
0ca5ea0150 | ||
|
|
11ca7c7b3e | ||
|
|
8f6c47527d | ||
|
|
dd28882b27 | ||
|
|
1b93a449b2 | ||
|
|
7cf2ec09b7 | ||
|
|
4d1952a406 | ||
|
|
b8d25e2327 | ||
|
|
89d7c7e572 | ||
|
|
75b19230eb | ||
|
|
7889ca1ca7 | ||
|
|
cec1b932bd | ||
|
|
2c7455ea2b | ||
|
|
edc9cc9278 | ||
|
|
51eafaeb4f | ||
|
|
556373d331 | ||
|
|
804cfb7482 | ||
|
|
143eae0729 | ||
|
|
29d4846de7 | ||
|
|
306178f371 | ||
|
|
371b541fbc | ||
|
|
902658fd21 | ||
|
|
0ca8dff974 | ||
|
|
a212b054e7 | ||
|
|
cccd1baa2f | ||
|
|
5dd25aed4d | ||
|
|
6ddc725b75 | ||
|
|
36ab7444a0 | ||
|
|
a8d004334a | ||
|
|
900398a632 | ||
|
|
f24bb8737d | ||
|
|
cb5e9b197a | ||
|
|
450e20ae78 | ||
|
|
5d42a76fe5 | ||
|
|
c5075a3db0 | ||
|
|
ad5bb5198b | ||
|
|
d306bc4a9a | ||
|
|
5c3b956542 | ||
|
|
ade8533f28 | ||
|
|
ad3413d6ad | ||
|
|
f3d0e3f4dd | ||
|
|
6e2e030ba7 | ||
|
|
613f993638 | ||
|
|
2566e3631b | ||
|
|
4ad6c0a8a9 | ||
|
|
0369ac450e | ||
|
|
ab16680cc8 | ||
|
|
8a344cd9b2 | ||
|
|
4e66fb4589 | ||
|
|
0043e3d623 | ||
|
|
ed168015bf | ||
|
|
1d450ef27e | ||
|
|
932e681d22 | ||
|
|
032bcd9fbe | ||
|
|
e113fa53a4 | ||
|
|
da212dc2a4 | ||
|
|
d1c5a62a9d | ||
|
|
0c777e10e5 | ||
|
|
b919b172b1 | ||
|
|
5dd8d05114 | ||
|
|
3400b32015 | ||
|
|
2e4becad60 | ||
|
|
3d485858d5 | ||
|
|
be93643808 | ||
|
|
f704352bae | ||
|
|
6683b46b07 | ||
|
|
a1fe458338 | ||
|
|
e6328e3207 | ||
|
|
8b1f703397 | ||
|
|
583c8add7e | ||
|
|
0266cc8e99 | ||
|
|
fac4132cb1 | ||
|
|
bf3864b2da | ||
|
|
7c21042858 | ||
|
|
d0fd9e4618 | ||
|
|
9b53230f83 | ||
|
|
d814b7b11e | ||
|
|
9f5421d3d4 | ||
|
|
6b9e5db3bf | ||
|
|
9eac3faf3d | ||
|
|
252de444fc | ||
|
|
c29b9cc937 | ||
|
|
6ac582a771 | ||
|
|
04b0c9f2cf | ||
|
|
6b2468f033 | ||
|
|
2737acb114 | ||
|
|
d03e9c48c5 | ||
|
|
08d9cfe82c | ||
|
|
e8ea8f91c3 | ||
|
|
e7cfa7fda5 | ||
|
|
b0b504d002 | ||
|
|
1da07ba60b | ||
|
|
cedebf8145 | ||
|
|
29832bb2c0 | ||
|
|
320d815d60 | ||
|
|
9f18401581 | ||
|
|
b3a2f0e3a3 | ||
|
|
c00dcd31d6 | ||
|
|
b836be2c84 | ||
|
|
f89c5cc072 | ||
|
|
ab7fa56bb3 | ||
|
|
7e624159ee | ||
|
|
857a791d68 | ||
|
|
171463faf5 | ||
|
|
2cafb49ffe | ||
|
|
ae12fed5fe | ||
|
|
be5547de53 | ||
|
|
cac50ef566 | ||
|
|
57a5736942 | ||
|
|
167eadf699 | ||
|
|
5676e420a6 | ||
|
|
f5edc9e973 | ||
|
|
4a305e1568 | ||
|
|
748ab221dd | ||
|
|
7d786cea8f | ||
|
|
1bf734338d | ||
|
|
98df1d6004 | ||
|
|
290e942f3e | ||
|
|
46d54f7e01 | ||
|
|
16e1715d06 | ||
|
|
52faa1bec5 | ||
|
|
c8a6465688 | ||
|
|
0273b7606e | ||
|
|
51022cb132 | ||
|
|
78f9d87ac3 | ||
|
|
e57135eaab | ||
|
|
5fbb663529 | ||
|
|
7db0f540bd | ||
|
|
c8f0afb775 | ||
|
|
51a5815098 | ||
|
|
18bd2eb729 | ||
|
|
f04fd61879 | ||
|
|
c1527cc4c8 | ||
|
|
cf1c1ed126 | ||
|
|
c4ba8f96e6 | ||
|
|
350884c627 | ||
|
|
aa756f6d68 | ||
|
|
1a77bf49f5 | ||
|
|
1b509c56fe | ||
|
|
038b10c74d | ||
|
|
39baa59625 | ||
|
|
3fc091ebb2 | ||
|
|
a557e133d3 | ||
|
|
7d4f10ccc6 | ||
|
|
39a15bef12 | ||
|
|
92c59f3ea1 | ||
|
|
226bd44e2c | ||
|
|
78d43a49c6 | ||
|
|
471651ac5a | ||
|
|
89515b0e30 | ||
|
|
1d61c0d970 | ||
|
|
35c3acbade | ||
|
|
58a2c6a13a | ||
|
|
bf0160adbd | ||
|
|
dfbc92dd3e | ||
|
|
85b0bbfd55 | ||
|
|
fb2a93dc05 | ||
|
|
4ac46bf19d | ||
|
|
4d73adb540 | ||
|
|
bdfeb1ae22 | ||
|
|
3fc116ab53 | ||
|
|
8db61a38cc | ||
|
|
0a70979934 | ||
|
|
1a2ccdf991 | ||
|
|
0adee56d76 | ||
|
|
fedd50efe4 | ||
|
|
d7de7dbe6b | ||
|
|
9fb8939efd | ||
|
|
fe8f671137 | ||
|
|
1a4de1482b | ||
|
|
090a2595e1 | ||
|
|
89dc7f817f | ||
|
|
fe0f26ce9c | ||
|
|
11349629f4 | ||
|
|
9a67652c0c | ||
|
|
b35521dfcb | ||
|
|
9faf306d1e | ||
|
|
d8393dace6 | ||
|
|
692e5275a3 | ||
|
|
1cfe517db8 | ||
|
|
e99e1dc2ac | ||
|
|
1bf4ebcabb | ||
|
|
ec4b264868 | ||
|
|
efb125b2d5 | ||
|
|
1c0fdbed59 | ||
|
|
37bb10e564 | ||
|
|
1507d5d85f | ||
|
|
2a86acfc50 | ||
|
|
e0ee01b92e | ||
|
|
6006917622 | ||
|
|
4ffcf6bf3b | ||
|
|
49cde59c4e | ||
|
|
ed7382995c | ||
|
|
a7faab53b9 | ||
|
|
a6aad25117 | ||
|
|
5f06084b19 | ||
|
|
8151fbcb2e | ||
|
|
5a8e46a6b8 | ||
|
|
c4997c52e2 | ||
|
|
ff12c22f50 | ||
|
|
ed7f09ab80 | ||
|
|
f68941992a | ||
|
|
afcd9285e6 | ||
|
|
22f78d5b33 | ||
|
|
5259142eb6 | ||
|
|
dc403ce8ab | ||
|
|
a6fddd6a2c | ||
|
|
819cd623f9 | ||
|
|
f185494de3 | ||
|
|
628a17ae72 | ||
|
|
08169b9180 | ||
|
|
1e79f2441c | ||
|
|
8680c0ba39 | ||
|
|
8a78b0dbd6 | ||
|
|
0232035e34 | ||
|
|
74f3df83db | ||
|
|
53fa0d2fe8 | ||
|
|
52aa9ab761 | ||
|
|
92cf6195b2 | ||
|
|
86a392991a | ||
|
|
30d426d667 | ||
|
|
5702c50680 | ||
|
|
e38d2e156d | ||
|
|
dfc18619a2 | ||
|
|
9e8f2c52a0 | ||
|
|
1c8f5b98f1 | ||
|
|
972470ad42 | ||
|
|
f7e3a055fb | ||
|
|
34f05fb8dc | ||
|
|
709fd34264 | ||
|
|
b888757092 | ||
|
|
0f42827047 | ||
|
|
e69d1556e1 | ||
|
|
141fbf7472 | ||
|
|
c9a3ae8149 | ||
|
|
e16a8d29ea | ||
|
|
fff50d0e50 | ||
|
|
360734b447 | ||
|
|
7e5edad32b | ||
|
|
fc47b47850 | ||
|
|
c7de058e1d | ||
|
|
0fff3c905c | ||
|
|
2fcb055c43 | ||
|
|
97e0d63102 | ||
|
|
5e04f0accd | ||
|
|
16835e3928 | ||
|
|
22e0ef1354 | ||
|
|
0141dc519c | ||
|
|
9aadc30b94 | ||
|
|
23a73fcb7c | ||
|
|
9a3b585110 | ||
|
|
09ee46860a | ||
|
|
d8f289182d | ||
|
|
dc8fa2153b | ||
|
|
32fb335832 | ||
|
|
58885854f1 | ||
|
|
3e87788541 | ||
|
|
5746df4d74 | ||
|
|
71d86c44e0 | ||
|
|
c5e50289e1 | ||
|
|
e62f1edada | ||
|
|
50e8db28b8 | ||
|
|
ef7c572018 | ||
|
|
1e962754cc | ||
|
|
0240453b07 | ||
|
|
38a80a95f2 | ||
|
|
b5e411d251 | ||
|
|
71a623276e | ||
|
|
1e76703144 | ||
|
|
6ffaf11226 | ||
|
|
fc0caec1a4 | ||
|
|
043f9a5025 | ||
|
|
822264a65e | ||
|
|
8e9a6a4f06 | ||
|
|
76f3909ec9 | ||
|
|
23e839b40e | ||
|
|
6a560fc7bf | ||
|
|
709d1c93a4 | ||
|
|
8d4b5f4bb7 | ||
|
|
9eb5a85e9e | ||
|
|
e7521445e9 | ||
|
|
bb050e2660 | ||
|
|
bc3b0eb491 | ||
|
|
7cea05d376 | ||
|
|
b8a3bdf6b5 | ||
|
|
638b6dba17 | ||
|
|
50ba8c6c39 | ||
|
|
94d15ebbef | ||
|
|
e150882231 | ||
|
|
80a659c90c | ||
|
|
17d96427ea | ||
|
|
7fdda788af | ||
|
|
182adbd9d9 | ||
|
|
4931c9cd9b | ||
|
|
a112409735 | ||
|
|
7aaa6dd986 | ||
|
|
3c39cfe645 | ||
|
|
6a46325e7e | ||
|
|
15c9688a5a | ||
|
|
d9f93ccc11 | ||
|
|
652e569afc | ||
|
|
c55372459f | ||
|
|
09f36e32f7 | ||
|
|
688d435d8e | ||
|
|
e7403d8551 | ||
|
|
60039441a1 | ||
|
|
97f4414cc4 | ||
|
|
df9595bc0d | ||
|
|
93533b7bc6 | ||
|
|
a99afa2ddf | ||
|
|
1e651a5145 | ||
|
|
79ecfa2573 | ||
|
|
e2b676d54c | ||
|
|
2b73bf35b6 | ||
|
|
2137b3a879 | ||
|
|
e52b3fd5bc | ||
|
|
0dfd2aa714 | ||
|
|
5d63c4c981 | ||
|
|
84d3f9ab9a | ||
|
|
1133dba9d8 | ||
|
|
cd2fdc2ffe | ||
|
|
57d017ed8e | ||
|
|
3ad2c559f1 | ||
|
|
c3facfa925 | ||
|
|
db8d9ebc72 | ||
|
|
3aef00af18 | ||
|
|
a4d576bae8 | ||
|
|
9320365ef6 | ||
|
|
0be7494042 | ||
|
|
df55f338ca | ||
|
|
8210efb548 | ||
|
|
43503e2fc9 | ||
|
|
448c3ae425 | ||
|
|
f7684a8a3e | ||
|
|
4f200ac71e | ||
|
|
4772559e8b | ||
|
|
a984ce5bba | ||
|
|
eee8a338e2 | ||
|
|
8894c0ef48 | ||
|
|
c5b8577d94 | ||
|
|
ce9def4a56 | ||
|
|
894601aad2 | ||
|
|
285c91e81b | ||
|
|
ba8ca93afc | ||
|
|
ad0dab2493 | ||
|
|
8b66316180 | ||
|
|
da4ee38914 | ||
|
|
62fde229e2 | ||
|
|
676115d214 | ||
|
|
f36d838df1 | ||
|
|
f32f3ab895 | ||
|
|
fc0432df8c | ||
|
|
797b5bcb53 | ||
|
|
b816f203cd | ||
|
|
714e2f86a1 | ||
|
|
ff75085f15 | ||
|
|
dbc8b18b80 | ||
|
|
efb2bbe8b2 | ||
|
|
8278c8eedd | ||
|
|
6f696a026b | ||
|
|
f558797246 | ||
|
|
c8671d1c2b | ||
|
|
7fd41e48b9 | ||
|
|
409d5f5966 | ||
|
|
5d1802faf7 | ||
|
|
a580bac385 | ||
|
|
c43214543f | ||
|
|
b3c568fb6f | ||
|
|
b34fe3200c | ||
|
|
dada8dbdeb | ||
|
|
e7d4077065 | ||
|
|
b1e54bfd86 | ||
|
|
85ad4c4766 | ||
|
|
3dce560759 | ||
|
|
969b8f0a7a | ||
|
|
ba8e738f40 | ||
|
|
4bc80fae8d | ||
|
|
ee5156704d | ||
|
|
986cd03732 | ||
|
|
937fdabaa9 | ||
|
|
c0fa97510e | ||
|
|
2fc6fe442d | ||
|
|
7ba14f5a57 | ||
|
|
352e4576fc | ||
|
|
210eebc0dc | ||
|
|
5904941361 | ||
|
|
b352815cf8 | ||
|
|
38a2751cd9 | ||
|
|
6388607d11 | ||
|
|
469276b8be | ||
|
|
2a863ee851 | ||
|
|
b6dae2fe57 | ||
|
|
d202bd3e50 | ||
|
|
dc2e44b2c0 | ||
|
|
32c481c28c | ||
|
|
8519c99706 | ||
|
|
5b1bac4c19 | ||
|
|
0a95a0674b | ||
|
|
2f11f6dc37 | ||
|
|
af66274c72 | ||
|
|
c5679b208f | ||
|
|
020fc179a5 | ||
|
|
dc9c1fc762 | ||
|
|
f0938d70b2 | ||
|
|
072761c5ba | ||
|
|
8aacd4d022 | ||
|
|
b6f9ca0021 | ||
|
|
a43f354511 | ||
|
|
43766406ae | ||
|
|
37dd1ae363 | ||
|
|
5a3effaef5 | ||
|
|
cd4bcce70f | ||
|
|
c1e7549b61 | ||
|
|
036ef25da9 | ||
|
|
9884c37ef4 | ||
|
|
23069d76eb | ||
|
|
b2d0e7fae2 | ||
|
|
30a039a031 | ||
|
|
8d67bb726c | ||
|
|
c4dec4cd4d | ||
|
|
f17b975fb9 | ||
|
|
7a3b7593d0 | ||
|
|
bad342b840 | ||
|
|
e3968a8f9d | ||
|
|
f577183c24 | ||
|
|
be58f2e68a | ||
|
|
99f81e82d5 | ||
|
|
7cfd3957de | ||
|
|
c51c88a17c | ||
|
|
e09af98dc5 | ||
|
|
89635f6583 | ||
|
|
14034642f1 | ||
|
|
4af7d975c1 | ||
|
|
d9884917fb | ||
|
|
95fd4e4c99 | ||
|
|
9457a55910 | ||
|
|
56e7dd0de0 | ||
|
|
f0dc882ae9 | ||
|
|
acb70d4440 | ||
|
|
167721bf8c | ||
|
|
4f438e5228 | ||
|
|
0f0aeb6d47 | ||
|
|
d1a6edad8a | ||
|
|
388feb7edb | ||
|
|
962b068c92 | ||
|
|
a32373bc3a | ||
|
|
94a5a4ac1e | ||
|
|
ed8eea12bd | ||
|
|
83bea8b654 | ||
|
|
d4d2db228e | ||
|
|
f8b8c91fe8 | ||
|
|
8451301a5a | ||
|
|
449b03f6bd | ||
|
|
3853ff725f | ||
|
|
ee66e3ef9e | ||
|
|
2643500957 | ||
|
|
88467a87a4 | ||
|
|
d1cdeb4a2d | ||
|
|
f5ced7fe39 | ||
|
|
023267ecab | ||
|
|
3d515a760c | ||
|
|
026c5987dd | ||
|
|
84fb3694f2 | ||
|
|
169dcbcd74 | ||
|
|
d28b6fbf1e | ||
|
|
9f7a2ef84e | ||
|
|
cc5ea60d2b | ||
|
|
8bc63494bc | ||
|
|
9acdeeed04 | ||
|
|
d2cdb2a209 | ||
|
|
36bad31355 | ||
|
|
427587e591 | ||
|
|
4f028d8201 | ||
|
|
d59c052554 | ||
|
|
93ebe91bc5 | ||
|
|
fadb8f695e | ||
|
|
8bb1518d09 | ||
|
|
e8d9d663a7 | ||
|
|
2d2648cf57 | ||
|
|
231036784d | ||
|
|
882b91b221 | ||
|
|
df346ab380 | ||
|
|
c7b8e82ada | ||
|
|
b8784863f2 | ||
|
|
3f67054f03 | ||
|
|
36d1b0a4bc | ||
|
|
748777b8eb | ||
|
|
acd8e0bce5 | ||
|
|
d2d068edff | ||
|
|
e2a7a7ce20 | ||
|
|
b25b1c45fb | ||
|
|
c3b0fa6c93 | ||
|
|
63a4e67174 | ||
|
|
88e37438fa | ||
|
|
5be1bf7d82 | ||
|
|
eb4689d2f1 | ||
|
|
e81549e5fd | ||
|
|
becaef36ff | ||
|
|
6aefa40ca2 | ||
|
|
250ef50361 | ||
|
|
5a1af60357 | ||
|
|
46db75308c | ||
|
|
33f5e13b29 | ||
|
|
54bb4797db | ||
|
|
7a14fbb221 | ||
|
|
18349f5ee4 | ||
|
|
22b93cdd0a | ||
|
|
63bc0b2ba1 | ||
|
|
c57a7c745d | ||
|
|
f2831b5f5c | ||
|
|
96f793f91b | ||
|
|
4e2a0d6e58 | ||
|
|
442c7f1f09 | ||
|
|
dbff4af7c6 | ||
|
|
93b09836da | ||
|
|
1782dc1565 | ||
|
|
cc16874d58 | ||
|
|
c8925b2ae3 | ||
|
|
fc043b9fcd | ||
|
|
ea86d33bc1 | ||
|
|
82f33b82e3 | ||
|
|
c645cb83a2 | ||
|
|
d6d79a2aa5 | ||
|
|
786568fd2f | ||
|
|
434f77a058 | ||
|
|
02f777a143 | ||
|
|
8025a84aeb | ||
|
|
c37a50d3b8 | ||
|
|
6dbbb265d1 | ||
|
|
02ce2ff74c | ||
|
|
28e5267132 | ||
|
|
f498b8a049 | ||
|
|
b31afe5c8c | ||
|
|
eac944dbea | ||
|
|
965b20c37a | ||
|
|
fba85754d7 | ||
|
|
3c69f98451 | ||
|
|
cad8a90124 | ||
|
|
b8d888b17f | ||
|
|
942d783e4e | ||
|
|
0e352cb466 | ||
|
|
50af38b952 | ||
|
|
1459c59cd0 | ||
|
|
e74f5b2991 | ||
|
|
0dec05fd70 | ||
|
|
3cff21b4d0 | ||
|
|
c380da7214 | ||
|
|
e2bf903c49 | ||
|
|
9cf64ba881 | ||
|
|
74d901b7ae | ||
|
|
b15cb46a6d | ||
|
|
89dc76e3b2 | ||
|
|
000b7d2651 | ||
|
|
9ffa372ba2 | ||
|
|
510cfcf1fc | ||
|
|
e7f6d93a1e | ||
|
|
a17c449075 | ||
|
|
c4da7a87f9 | ||
|
|
4a8b2f826d | ||
|
|
4901e4fb80 | ||
|
|
ce3a0157cb | ||
|
|
a146b06e4b | ||
|
|
dc78a3e56d | ||
|
|
86c524dade | ||
|
|
6889edcfc4 | ||
|
|
3cd5934bad | ||
|
|
064104f23a | ||
|
|
e80b1c264c | ||
|
|
03c3ee514b | ||
|
|
c93d0f0d59 | ||
|
|
85793d38bc | ||
|
|
49b4a85d55 | ||
|
|
5e8a9340f1 | ||
|
|
2b97c01bd6 | ||
|
|
39e3a5a708 | ||
|
|
df13d993f7 | ||
|
|
36c62763e5 | ||
|
|
ad5651ffb2 | ||
|
|
717b091b49 | ||
|
|
4376bbe40b | ||
|
|
2382d2e21c | ||
|
|
520ccfcd06 | ||
|
|
0bdc5dfe95 | ||
|
|
6a656a1b0a | ||
|
|
5ad4079dbf | ||
|
|
bfa46b795f | ||
|
|
ecaa9c275c | ||
|
|
93fd1757f8 | ||
|
|
71d0958398 | ||
|
|
ed89d94d7a | ||
|
|
135132e5f0 | ||
|
|
1ad75f5c1b | ||
|
|
21c6f9e394 | ||
|
|
5a7e954aee | ||
|
|
c1117e260c | ||
|
|
b0ea150e94 | ||
|
|
0e3bdc02ea | ||
|
|
e5a075a329 | ||
|
|
30c7a64619 | ||
|
|
ab12f29ebf | ||
|
|
a0c3a82c76 | ||
|
|
085a281f8a | ||
|
|
1f871c24b9 | ||
|
|
9f46b51985 | ||
|
|
fc572b8717 | ||
|
|
4404601fa3 | ||
|
|
faf7e28119 | ||
|
|
0303facfd9 | ||
|
|
86e056a736 | ||
|
|
75105dc1fc | ||
|
|
14b4979c98 | ||
|
|
f956c64765 | ||
|
|
8b3f954ad8 | ||
|
|
a013cfcd1e | ||
|
|
65553e6c43 | ||
|
|
401c881c00 | ||
|
|
cf0e4d24a1 | ||
|
|
21810f3b39 | ||
|
|
0b211ab0ba | ||
|
|
30c7cf3512 | ||
|
|
bf8839a560 | ||
|
|
818998c532 | ||
|
|
98f54029d0 | ||
|
|
d41f2c138b | ||
|
|
50f07a6a0d | ||
|
|
b10116febb | ||
|
|
17b5d732e4 | ||
|
|
5b16b1d827 | ||
|
|
e5fe962130 | ||
|
|
00f03d9bb7 | ||
|
|
f7c285af20 | ||
|
|
f6e3121c76 | ||
|
|
a95aa81850 | ||
|
|
ffa5825658 | ||
|
|
df5b90cc7c | ||
|
|
9dde644fd0 | ||
|
|
d525a1536c | ||
|
|
54f18f3f0d | ||
|
|
ce4f26a5d5 | ||
|
|
177a7429f1 | ||
|
|
f672d734f9 | ||
|
|
faf5a38f1c | ||
|
|
03c4267b84 | ||
|
|
ab08621808 | ||
|
|
d06c0e5c5d | ||
|
|
32e76298e4 | ||
|
|
752163a5c0 | ||
|
|
6568fa23f8 | ||
|
|
9c69288304 | ||
|
|
3a3346e750 | ||
|
|
47ab73d9a4 | ||
|
|
9555385325 | ||
|
|
f80a03afc7 | ||
|
|
c1b64770d1 | ||
|
|
400f106d62 | ||
|
|
1cc20716ae | ||
|
|
fc7d123871 | ||
|
|
2a8a014ad0 | ||
|
|
861f452d26 | ||
|
|
64a7d5e4fd | ||
|
|
7c45423969 | ||
|
|
480f70d7e2 | ||
|
|
7f182896ca | ||
|
|
1319fe2eb5 | ||
|
|
f5c231aa80 | ||
|
|
84d468333a | ||
|
|
dfdbb0a061 | ||
|
|
27365abbe3 | ||
|
|
f3aed46c94 | ||
|
|
7e0ea176cf | ||
|
|
791e13e7bf | ||
|
|
9dff3ac749 | ||
|
|
45f207164d | ||
|
|
bea6f3ec14 | ||
|
|
1edc883ca2 | ||
|
|
116825d604 | ||
|
|
26bc5ef091 | ||
|
|
8c228287aa | ||
|
|
5c7c682d41 | ||
|
|
bfc2eca041 | ||
|
|
b043b48e6e | ||
|
|
d7c03d7356 | ||
|
|
c41f6aebf4 | ||
|
|
5e6cda7c5e | ||
|
|
9c71b53abc | ||
|
|
3f96e04a7b | ||
|
|
90eeeedf52 | ||
|
|
6649362fec | ||
|
|
39c8889585 | ||
|
|
b5b1a64f0c | ||
|
|
cd108b5f57 | ||
|
|
780f3f8552 | ||
|
|
afea5eb69e | ||
|
|
b72cf5d364 | ||
|
|
c133686dde | ||
|
|
e216fa98ef | ||
|
|
8f9d8e188f | ||
|
|
800751cbe0 | ||
|
|
14bc73cde9 | ||
|
|
41307046be | ||
|
|
c259c79384 | ||
|
|
49f69facaf | ||
|
|
da2b6acf91 | ||
|
|
fbdbe848c5 | ||
|
|
cc41caede9 | ||
|
|
ee373eb6fb | ||
|
|
d4b18b1e27 | ||
|
|
94e2fe2cbb | ||
|
|
c93658ee04 | ||
|
|
ad7ee0b716 | ||
|
|
b2fad7a567 | ||
|
|
e80adc2799 | ||
|
|
ac111224ae | ||
|
|
0813bb1a28 | ||
|
|
4d1ad8f749 | ||
|
|
80b444e531 | ||
|
|
3a6468cd0a | ||
|
|
c9fdb215f3 | ||
|
|
85ff5f2d10 | ||
|
|
be3597524f | ||
|
|
5717dbda4f | ||
|
|
588eec4672 | ||
|
|
cc3d0d8211 | ||
|
|
1550efaf7e | ||
|
|
007300b881 | ||
|
|
454f072f13 | ||
|
|
b3be6c75c3 | ||
|
|
f8774f314b | ||
|
|
3db35ad343 | ||
|
|
0e42a779e2 | ||
|
|
d34e829811 | ||
|
|
ebd988702e | ||
|
|
1232398742 | ||
|
|
6dbcd47143 | ||
|
|
0e07482e26 | ||
|
|
6bb4800ecd | ||
|
|
37e1ac71b0 | ||
|
|
869f96816c | ||
|
|
35a7008444 | ||
|
|
67f80ee111 | ||
|
|
083a58753b | ||
|
|
b652357a5c | ||
|
|
49a52140b0 | ||
|
|
e37f84a227 | ||
|
|
50fc83d478 | ||
|
|
a3da1c54eb | ||
|
|
b8471e3cf1 | ||
|
|
4bd645d5c6 | ||
|
|
83446c0694 | ||
|
|
b4c30fc07b | ||
|
|
8325f5bbae | ||
|
|
9eb1196526 | ||
|
|
5fe5d4b6fc | ||
|
|
34af44b8d3 | ||
|
|
643a20fec3 | ||
|
|
576b931090 | ||
|
|
2cc1f044df | ||
|
|
c651392643 | ||
|
|
9a669176ac | ||
|
|
b902831db8 | ||
|
|
90a1de383a | ||
|
|
83f9d42d31 | ||
|
|
94136531a5 | ||
|
|
b8b1e6ba62 | ||
|
|
d6911337f3 | ||
|
|
d2cbb72490 | ||
|
|
c835b4d363 | ||
|
|
ece4480200 | ||
|
|
57f8d90e38 | ||
|
|
da2b2701fe | ||
|
|
6d7fba3abf | ||
|
|
90798c161b | ||
|
|
65e270ff57 | ||
|
|
4912e19281 | ||
|
|
6402564f44 | ||
|
|
a06d5dd7bb | ||
|
|
15e7486ca3 | ||
|
|
ac462cfce9 | ||
|
|
d921306dd8 | ||
|
|
05417705ed | ||
|
|
1c7d9e3d75 | ||
|
|
97dd928ca4 | ||
|
|
03949f7d60 | ||
|
|
effe477351 | ||
|
|
12ac858242 | ||
|
|
eec1937f52 | ||
|
|
906cd48fb7 | ||
|
|
78beecf6f1 | ||
|
|
06a0646541 | ||
|
|
0499a6c6f9 | ||
|
|
0e62f2ee85 | ||
|
|
2ba4c36af0 | ||
|
|
688e6ad79f | ||
|
|
f8ee0ce185 | ||
|
|
a226e06ae8 | ||
|
|
e0e51b51b7 | ||
|
|
9e9e3d43a8 | ||
|
|
7691a9bdad | ||
|
|
22020cae49 | ||
|
|
6cd910b0f6 | ||
|
|
b5e9811063 | ||
|
|
7668c7dd00 | ||
|
|
d71f743e85 | ||
|
|
73ef0222ac | ||
|
|
6152387096 | ||
|
|
b93ffe3b44 | ||
|
|
478eb7c5b5 | ||
|
|
6c40d8bd8b | ||
|
|
7026fe4d5a | ||
|
|
9a4491dc50 | ||
|
|
0d835e5014 | ||
|
|
29b5ffb58d | ||
|
|
a179ec485b | ||
|
|
f220a577e0 | ||
|
|
b22b1d4794 | ||
|
|
d2d19e3fbc | ||
|
|
19ef290499 | ||
|
|
f6584b45ed | ||
|
|
bad97a8b5b | ||
|
|
7116df15d6 | ||
|
|
ffbb018d12 | ||
|
|
a4dcdfc403 | ||
|
|
87d901b1ae | ||
|
|
9126651cb9 | ||
|
|
3996ce8ce5 | ||
|
|
6340423b32 | ||
|
|
0d921dc1fc | ||
|
|
25afea4f9c | ||
|
|
ba46bf3af5 | ||
|
|
416b4885f9 | ||
|
|
9a10ee0efa | ||
|
|
d63b78f0e7 | ||
|
|
99411241f6 | ||
|
|
941fb1349b | ||
|
|
a470925305 | ||
|
|
e3ac292af1 | ||
|
|
260b9a8d0d | ||
|
|
530407f9b2 | ||
|
|
52e7b50e9b | ||
|
|
23aa097d0a | ||
|
|
0bea153aa0 | ||
|
|
00be58e65d | ||
|
|
a5cc985bc2 | ||
|
|
afba54fb5e | ||
|
|
46150a2da4 | ||
|
|
842988c2ad | ||
|
|
ebad34ae51 | ||
|
|
bceb359438 | ||
|
|
19a32134ab | ||
|
|
e44a71daf2 | ||
|
|
573750a43d | ||
|
|
dd4c73b41f | ||
|
|
094326220f | ||
|
|
729a071284 | ||
|
|
ee7857a560 | ||
|
|
4cba604712 | ||
|
|
18a62797d9 | ||
|
|
5d4ac62c9e | ||
|
|
2ef8c41b48 | ||
|
|
891831a96a | ||
|
|
11d18fa1af | ||
|
|
ad69cbc681 | ||
|
|
10995de7bd | ||
|
|
c4d78c06eb | ||
|
|
eb252321a7 | ||
|
|
04dd1447ee | ||
|
|
b14edbe1f4 | ||
|
|
c697e52c0c | ||
|
|
b6911da8f0 | ||
|
|
038448865b | ||
|
|
9e44fdbc55 | ||
|
|
e088f0f31e | ||
|
|
6101feda10 | ||
|
|
7a3fa68468 | ||
|
|
16a11638a7 | ||
|
|
43932492a7 | ||
|
|
8d08ffc4ae | ||
|
|
a5a4ee4bfe | ||
|
|
e66f3a80e8 | ||
|
|
e69efacb47 | ||
|
|
528b361e34 | ||
|
|
a77bb3d341 | ||
|
|
6b17385ad7 | ||
|
|
33fc01ef4b | ||
|
|
482324c0bd | ||
|
|
0ab13a3a70 | ||
|
|
699fb73726 | ||
|
|
ddba29cc09 | ||
|
|
1cdb9ef47c | ||
|
|
ade3305b4c | ||
|
|
bd2bd37bc4 | ||
|
|
ee6f55193e | ||
|
|
1868df61ea | ||
|
|
dfa1fc7b09 | ||
|
|
a52b0495f1 | ||
|
|
ce00eee29f | ||
|
|
858bfade6a | ||
|
|
4535cf9274 | ||
|
|
84d1fa54ec | ||
|
|
7cf4721efa | ||
|
|
6578838587 | ||
|
|
46b7c4717f | ||
|
|
52bcf63554 | ||
|
|
37ec97550e | ||
|
|
55e20abea8 | ||
|
|
ec7d81dc7a | ||
|
|
e93387b719 | ||
|
|
050b476fc3 | ||
|
|
5b5ffd7eaf | ||
|
|
39bf5fdc05 | ||
|
|
09ff43ef0a | ||
|
|
5a02aeaa89 | ||
|
|
2a3a91af42 | ||
|
|
d4a64e93e0 | ||
|
|
c126b64892 | ||
|
|
526814b9ed | ||
|
|
bc3b790f13 | ||
|
|
1fb0765443 | ||
|
|
c4b85f9bda | ||
|
|
161b5530b9 | ||
|
|
e03979ebdc | ||
|
|
356c0ab594 | ||
|
|
85152bac34 | ||
|
|
c832888d56 | ||
|
|
cac1b87b44 | ||
|
|
30f71b2ed6 | ||
|
|
9f8e9a1916 | ||
|
|
8519fe57ae | ||
|
|
1442a3853c | ||
|
|
2658ddf868 | ||
|
|
f95eecfcc6 | ||
|
|
1e598cca31 | ||
|
|
f3eaeb96f0 | ||
|
|
91fac8049c | ||
|
|
b1325a7514 | ||
|
|
150b83b4f3 | ||
|
|
3a3a5dc071 | ||
|
|
9b76a5bd66 | ||
|
|
850a1f9155 | ||
|
|
ef6c8a38d0 | ||
|
|
e228cb6648 | ||
|
|
3a53cac607 | ||
|
|
df4bd57c82 | ||
|
|
de737fc72d | ||
|
|
ccea7a5783 | ||
|
|
5b84e1528a | ||
|
|
7382addfe4 | ||
|
|
b8dd6822ac | ||
|
|
8c47b85b9b | ||
|
|
4d2666a0e3 | ||
|
|
fc18eaa292 | ||
|
|
22199cdd87 | ||
|
|
76820f25c6 | ||
|
|
bac4984496 | ||
|
|
1414c5feac | ||
|
|
c344f63154 | ||
|
|
77ea28bef2 | ||
|
|
bf63b15e07 | ||
|
|
ce05504278 | ||
|
|
95b90974a2 | ||
|
|
1adbef7352 | ||
|
|
3109928dcd | ||
|
|
24dd63789c | ||
|
|
27225e6b5e | ||
|
|
2994978fdb | ||
|
|
ffd864dc72 | ||
|
|
53a2c88d4b | ||
|
|
444ae73ffb | ||
|
|
9f5f4b9c91 | ||
|
|
ffd2af0257 | ||
|
|
b983845745 | ||
|
|
cfcc6e1029 | ||
|
|
6fea4377b2 | ||
|
|
e01230e425 | ||
|
|
f66db81edd | ||
|
|
a47471d0ae | ||
|
|
06b5a91cd3 | ||
|
|
8aacf3497e | ||
|
|
7fc5e304e3 | ||
|
|
65536febd5 | ||
|
|
60f7910840 | ||
|
|
829c3c612e | ||
|
|
355c7b59aa | ||
|
|
6508cdc823 | ||
|
|
9ef9f603a3 | ||
|
|
e76f881c67 | ||
|
|
4292d2ce86 | ||
|
|
ae927c5600 | ||
|
|
57e2eed771 | ||
|
|
ca807b892f | ||
|
|
9bc015d10e | ||
|
|
59b204c3bc | ||
|
|
4ac2472a62 | ||
|
|
054b9c2402 | ||
|
|
96f267cbfd | ||
|
|
2018163107 | ||
|
|
253b5b1fb7 | ||
|
|
0be2f1b295 | ||
|
|
a8838fc611 | ||
|
|
d02636e370 | ||
|
|
f3c1b09188 | ||
|
|
7bac69f360 | ||
|
|
5a3fdd225e | ||
|
|
4660f58653 | ||
|
|
5fabca3616 | ||
|
|
edb74dae87 | ||
|
|
31b833b46a | ||
|
|
21bc1c51ba | ||
|
|
4f6cab9a45 | ||
|
|
33626fa47c | ||
|
|
9fcc1e17f2 | ||
|
|
a5f48f8af8 | ||
|
|
3dfa526ec6 | ||
|
|
dc28066017 | ||
|
|
2f2edfbde8 | ||
|
|
3beac98499 | ||
|
|
f70ce9dd26 | ||
|
|
e301b9d399 | ||
|
|
42f92ecd7a | ||
|
|
27eb0d04b9 | ||
|
|
112d13c127 | ||
|
|
16afc9d0f8 | ||
|
|
5b4889b46b | ||
|
|
f7eed0e2af | ||
|
|
cce287e8a6 | ||
|
|
9a001ebacc | ||
|
|
79ab4c02bb | ||
|
|
b6de70a3a6 | ||
|
|
15cafe2d78 | ||
|
|
90197abc5c | ||
|
|
2147436a2a | ||
|
|
8ef0c66966 | ||
|
|
2779ad6c6f | ||
|
|
f30fe3c746 | ||
|
|
f71496693f | ||
|
|
e4cd5dbf1f | ||
|
|
ffd5defcb6 | ||
|
|
0b81421124 | ||
|
|
0f73b9e07f | ||
|
|
d3078699d6 | ||
|
|
cc39f9f891 | ||
|
|
70608df843 | ||
|
|
e7f2c6b5ba | ||
|
|
420fc9aa24 | ||
|
|
200012fd41 | ||
|
|
e3c858278b | ||
|
|
1ec7c3afa4 | ||
|
|
6ab0b60f12 | ||
|
|
5ec38b44b0 | ||
|
|
39c2294d43 | ||
|
|
3b92481133 | ||
|
|
6b8d24d2f5 | ||
|
|
62d6ff61ab | ||
|
|
f7c056145a | ||
|
|
451f54b036 | ||
|
|
ef378de883 | ||
|
|
2f2b63da4a | ||
|
|
01d8f2c7e6 | ||
|
|
330d553ffb | ||
|
|
f1d0895eb1 | ||
|
|
4eaf6415b7 | ||
|
|
f0650bf64f | ||
|
|
44856c6207 | ||
|
|
dcbf6e43c7 | ||
|
|
0d22da672d | ||
|
|
a62471b9b8 | ||
|
|
f673f2c5bc | ||
|
|
a7caae5c17 | ||
|
|
d0deefd0d7 | ||
|
|
9810da6982 | ||
|
|
cb93a3bee3 | ||
|
|
aeacc3e39d | ||
|
|
e1c861cc8a | ||
|
|
37ee05a576 | ||
|
|
07a2bd817d | ||
|
|
78c171e0c2 | ||
|
|
1ae436cb08 | ||
|
|
b0d3a84d1e | ||
|
|
1acd9ca981 | ||
|
|
72467b6b9c | ||
|
|
862c85387c | ||
|
|
eeeca3e21c | ||
|
|
4ee4af7fd4 | ||
|
|
d131cc1871 | ||
|
|
bd21ee318d | ||
|
|
02e7fc0bca | ||
|
|
51ba46970e | ||
|
|
9d65b44ab7 | ||
|
|
350d51a92d | ||
|
|
2a8202caa7 | ||
|
|
9e4591554f | ||
|
|
e98ecfd1f9 | ||
|
|
1cbf5c4359 | ||
|
|
0446faeb6f | ||
|
|
0417ea23c3 | ||
|
|
de9886e011 | ||
|
|
770e57c361 | ||
|
|
d80a00701d | ||
|
|
62d3cd8795 | ||
|
|
ad9ec22fd1 | ||
|
|
293985ec10 | ||
|
|
105598ac8e | ||
|
|
ff93f2220a | ||
|
|
33fddcb289 | ||
|
|
6a09bf2c54 | ||
|
|
2913f5bbbe | ||
|
|
671bdb09b8 | ||
|
|
353a0d14a4 | ||
|
|
e0fd83211b | ||
|
|
2c94d9cc37 | ||
|
|
f7dec412ec | ||
|
|
f7c756d215 | ||
|
|
617635a989 | ||
|
|
ea3b2c4713 | ||
|
|
15455a7e68 | ||
|
|
ad5cedc08c | ||
|
|
328f0c6da8 | ||
|
|
8ef818f581 | ||
|
|
48dace64e4 | ||
|
|
ab5837c7c4 | ||
|
|
9fbdbc1844 | ||
|
|
54129cecc2 | ||
|
|
69c514d1a4 | ||
|
|
7047d12eba | ||
|
|
8ab61637f0 | ||
|
|
7e0bd92593 | ||
|
|
99d9a9f44e | ||
|
|
9c55111e14 | ||
|
|
b1d6ba57ad | ||
|
|
47e9330646 | ||
|
|
e16a3d503c | ||
|
|
dc9c93b347 | ||
|
|
faceff620a | ||
|
|
a5a6a58556 | ||
|
|
45fdab27c9 | ||
|
|
8b360479f4 | ||
|
|
b170f2f69a | ||
|
|
7f1abab9ae | ||
|
|
2ec7bc878e | ||
|
|
e97d9bb095 | ||
|
|
2f3b02e870 | ||
|
|
11b7a45c32 | ||
|
|
fb10d2f9c2 | ||
|
|
60bb7f66cf | ||
|
|
ad5143169a | ||
|
|
d68c90a429 | ||
|
|
8a51549395 | ||
|
|
9f997fe1fa | ||
|
|
00f1f6d86b | ||
|
|
be72728000 | ||
|
|
f558126854 | ||
|
|
08ed1500ac | ||
|
|
f0926f1cc7 | ||
|
|
fe3f6aa22b | ||
|
|
1325f75e81 | ||
|
|
4b1d3a7b6f | ||
|
|
404afcdfd6 | ||
|
|
99793180f1 | ||
|
|
c91f9beddb | ||
|
|
c9c5ccaa6d | ||
|
|
da42de7ac8 | ||
|
|
3c9a6ad218 | ||
|
|
f0389d6b47 | ||
|
|
76eab21738 | ||
|
|
3b32c57d94 | ||
|
|
d119032cb9 | ||
|
|
397a04b49c | ||
|
|
d32c8e9457 | ||
|
|
ebb2974ca7 | ||
|
|
cbb1131a5d | ||
|
|
d9de7fe3b0 | ||
|
|
24ba692884 | ||
|
|
960bbbde29 | ||
|
|
eb79b79c35 | ||
|
|
8212ed6d87 | ||
|
|
c639cb5285 | ||
|
|
a88fd1c669 | ||
|
|
b053f23d15 | ||
|
|
9d32a89491 | ||
|
|
cb9a147fff | ||
|
|
c9fddedbf1 | ||
|
|
530bbbe4c5 | ||
|
|
73e5a9f45b | ||
|
|
72d4e947b6 | ||
|
|
41750c3639 | ||
|
|
52b36f38a0 | ||
|
|
58ceb454dd | ||
|
|
9e5a2db4c5 | ||
|
|
781f283c89 | ||
|
|
f1cd085727 | ||
|
|
3aba855bd6 | ||
|
|
7e3abb27a4 | ||
|
|
f0c11daa37 | ||
|
|
167c174f4d | ||
|
|
0173060eaf | ||
|
|
4edd25ff5d | ||
|
|
2a4e098645 | ||
|
|
7c5019c30f | ||
|
|
c13fe3feb6 | ||
|
|
0ced60e712 | ||
|
|
d0764d5854 | ||
|
|
f7905e3bc2 | ||
|
|
3e770792a4 | ||
|
|
8f9934a7f4 | ||
|
|
b921e7d595 | ||
|
|
a9cb2d2efd | ||
|
|
fe519b3acf | ||
|
|
30cc4a1668 | ||
|
|
a71e4d8970 | ||
|
|
66e7141415 | ||
|
|
6f0cdc864f | ||
|
|
4f9deaaf09 | ||
|
|
380745a814 | ||
|
|
ce4e335412 | ||
|
|
274b8181d1 | ||
|
|
767bdcc2ce | ||
|
|
c00cde8bcc | ||
|
|
5859a18b3a | ||
|
|
375124642d | ||
|
|
af9e6dea68 | ||
|
|
78f2267b06 | ||
|
|
0a34f22240 | ||
|
|
b0651fcaed | ||
|
|
d59faa2a08 | ||
|
|
6a3bc37dc1 | ||
|
|
2ea7796b26 | ||
|
|
cf0057bac4 | ||
|
|
fafb325a88 | ||
|
|
6b69b4671f | ||
|
|
eaa95ee840 | ||
|
|
a86ba1fdf6 | ||
|
|
09e0d2d15b | ||
|
|
6d9fa6418d | ||
|
|
6010f5b7a6 | ||
|
|
9c164e1cea | ||
|
|
880fba8dce | ||
|
|
17d57ee49a | ||
|
|
234949a627 | ||
|
|
ff34671526 | ||
|
|
6e22983d98 | ||
|
|
18254c30ec | ||
|
|
922c955239 | ||
|
|
618a325057 | ||
|
|
d81cb7f866 | ||
|
|
f71a4d5381 | ||
|
|
282cb60a66 | ||
|
|
58dbac4422 | ||
|
|
53e228465b | ||
|
|
06426e4fd7 | ||
|
|
c55a97c24d | ||
|
|
0d61289bf3 | ||
|
|
bf10cbc70b | ||
|
|
2d945e426b | ||
|
|
13250887fa | ||
|
|
3507c22968 | ||
|
|
cc3f98ebcd | ||
|
|
4116e2d6ac | ||
|
|
48eab6b4a7 | ||
|
|
25866b5369 | ||
|
|
c4dd06fa45 | ||
|
|
4846d211e0 | ||
|
|
a586ad1237 | ||
|
|
b701c9c464 | ||
|
|
d0f20ed2bf | ||
|
|
0f64a2f3b0 | ||
|
|
bd83fba6cd | ||
|
|
15b9255a25 | ||
|
|
6794d21487 | ||
|
|
118c4432ec | ||
|
|
ccda429bf1 | ||
|
|
590937d2a0 | ||
|
|
241827dd80 | ||
|
|
77b4e0b4bd | ||
|
|
6a53cb32d8 | ||
|
|
f12ff3b15e | ||
|
|
69ad8676b7 | ||
|
|
d0fd756f6b | ||
|
|
7f8ab67e9e | ||
|
|
0e68bb3d2c | ||
|
|
d5b3933752 | ||
|
|
0b7206a92d | ||
|
|
e875f306fd | ||
|
|
8ed5e5ee28 | ||
|
|
948b0d40f6 | ||
|
|
48f4feb7a7 | ||
|
|
ddc0a32bbc | ||
|
|
10a7c54364 | ||
|
|
a66fa9d844 | ||
|
|
a16f2ac15c | ||
|
|
b699797a40 | ||
|
|
a6e9520d06 | ||
|
|
f8310b1296 | ||
|
|
353bf99cd5 | ||
|
|
a478e4acc9 | ||
|
|
d810e0ba7d | ||
|
|
1e51ffdb72 | ||
|
|
7da1d1fc64 | ||
|
|
957697c383 | ||
|
|
107eb72225 | ||
|
|
810b77f9da | ||
|
|
5e3bc2049b | ||
|
|
229500347d | ||
|
|
1f461db0a1 | ||
|
|
6c55aafee3 | ||
|
|
d57e55b719 | ||
|
|
bf772e5fe5 | ||
|
|
413f3356ce | ||
|
|
f147326fe0 | ||
|
|
30c59644b6 | ||
|
|
3fa5acf2e6 | ||
|
|
3e6ded8823 | ||
|
|
612017aaa2 | ||
|
|
ef57d7e7b6 | ||
|
|
a8d0db5036 | ||
|
|
ad6b5f8fc1 | ||
|
|
e3cb6a0aec | ||
|
|
cbf18a7dc4 | ||
|
|
01559410a9 | ||
|
|
f2ab94fa7f | ||
|
|
d7394a8369 | ||
|
|
9feef11d6f | ||
|
|
dc4e5ddc3c | ||
|
|
8600781bb6 | ||
|
|
bdc7bbdc9d | ||
|
|
73badef594 | ||
|
|
9474e6c08c | ||
|
|
653ded0e6f | ||
|
|
e34bec7dee | ||
|
|
610b560fb5 | ||
|
|
0a03ddb8a7 | ||
|
|
f31790631a | ||
|
|
e07128760e | ||
|
|
dd02ae313d | ||
|
|
d14b0b6843 | ||
|
|
f763c8a777 | ||
|
|
4231b040d8 | ||
|
|
67c587e673 | ||
|
|
3978e24fd8 | ||
|
|
c45de0c032 | ||
|
|
ff935efea1 | ||
|
|
808464f47d | ||
|
|
c986a6c4dd | ||
|
|
17c0479343 | ||
|
|
d93238912a | ||
|
|
d6ef0956e6 | ||
|
|
05db0aad29 | ||
|
|
89bbdfa1fe | ||
|
|
871bda6198 | ||
|
|
20732c9206 | ||
|
|
2a34a3ebb6 | ||
|
|
3f04247a53 | ||
|
|
ca0a1f8f8b | ||
|
|
d8a9f0ca12 | ||
|
|
c2116b841e | ||
|
|
700cf69f18 | ||
|
|
ed5dee5218 | ||
|
|
b224dfdfac | ||
|
|
e7da68547f | ||
|
|
9a785ceb2e | ||
|
|
fca1cd5a1c | ||
|
|
24b862e32e | ||
|
|
2ec9043cf2 | ||
|
|
1102d63469 | ||
|
|
b89f39d78c | ||
|
|
7ba479c9c9 | ||
|
|
8ad6a2980c | ||
|
|
d3b6ed78d9 | ||
|
|
8bd5605c2a | ||
|
|
08dc2fcf33 | ||
|
|
caa8d16371 | ||
|
|
bce92b3d85 | ||
|
|
67858bf300 | ||
|
|
7157e7e77d | ||
|
|
cf5074bdc5 | ||
|
|
fda44063ce | ||
|
|
f9becda02c | ||
|
|
fb9fc952c6 | ||
|
|
18451edfe9 | ||
|
|
8c73cac72f | ||
|
|
c54cedf14b | ||
|
|
8ef4cdc9c3 | ||
|
|
c0213e84f6 | ||
|
|
29de6d89d4 | ||
|
|
c9bf38ce36 | ||
|
|
338eb75bab | ||
|
|
31b1b453b0 | ||
|
|
aaf0e145eb | ||
|
|
089b3e13fd | ||
|
|
e9da2ce12a | ||
|
|
92048ac17b | ||
|
|
5e8561a578 | ||
|
|
d2f5e13074 | ||
|
|
820178f006 | ||
|
|
0a36a91e6d | ||
|
|
5013a92795 | ||
|
|
d81ecfec32 | ||
|
|
e99d7e2c3c | ||
|
|
d417984ff3 | ||
|
|
d38b3e641b | ||
|
|
28ce491dd5 | ||
|
|
c260d72125 | ||
|
|
d1d1b3156d | ||
|
|
472064b751 | ||
|
|
95ab9a0b70 | ||
|
|
4b03f6a039 | ||
|
|
c3460727fa | ||
|
|
2cc1850212 | ||
|
|
2d7443acaf | ||
|
|
13d0b0940c | ||
|
|
c101797924 | ||
|
|
83b55f8e3f | ||
|
|
b3b6362cd9 | ||
|
|
fc9af32d5f | ||
|
|
4cd1025011 | ||
|
|
5233fe8abc | ||
|
|
041e31ea78 | ||
|
|
7a3e881099 | ||
|
|
631bf42f84 | ||
|
|
1f704a7019 | ||
|
|
d295c88474 | ||
|
|
1dd9da4dff | ||
|
|
f2eb0c8427 | ||
|
|
c8ba11faf8 | ||
|
|
a2e243d992 | ||
|
|
c588fff5ca | ||
|
|
87f9599fea | ||
|
|
0459599b1d | ||
|
|
9447b1a696 | ||
|
|
0ccb7443c2 | ||
|
|
02cf27091f | ||
|
|
fdfbd04503 | ||
|
|
866c18200a | ||
|
|
c1cada49d4 | ||
|
|
7bf550a75f | ||
|
|
9c540c03aa | ||
|
|
b3df46db19 | ||
|
|
7ca615a1c1 | ||
|
|
c83db557a6 | ||
|
|
d54594f11d | ||
|
|
434e38608f | ||
|
|
871f090ca0 | ||
|
|
d1d235e025 | ||
|
|
e822a5fd53 | ||
|
|
7b82a4ae50 | ||
|
|
c532e9f2eb | ||
|
|
3fd034816e | ||
|
|
bb4b868c79 | ||
|
|
3b3da11a36 | ||
|
|
f2cbb5306b | ||
|
|
94ede1b324 | ||
|
|
0367248338 | ||
|
|
936db30e58 | ||
|
|
4822f0dd11 | ||
|
|
456d220829 | ||
|
|
b459ba6ea7 | ||
|
|
a19ef9bd16 | ||
|
|
59cec88a28 | ||
|
|
3ebc75af80 | ||
|
|
4dce474e03 | ||
|
|
31a18da578 | ||
|
|
8c499850fc | ||
|
|
6b6998a247 | ||
|
|
a6cb0fc856 | ||
|
|
e36b93e87b | ||
|
|
1e3723b8bb | ||
|
|
412372289e | ||
|
|
96f7e66073 | ||
|
|
6040f8f263 | ||
|
|
9761b6e14a | ||
|
|
cb49910ed2 | ||
|
|
62bd742673 | ||
|
|
42d0a3d734 | ||
|
|
f0f8681455 | ||
|
|
c801afddcb | ||
|
|
20e0e1333e | ||
|
|
a6b373fec4 | ||
|
|
41c77720bb | ||
|
|
92e6340120 | ||
|
|
1221f63cbd | ||
|
|
0f24418891 | ||
|
|
f477c0ab87 | ||
|
|
9358691901 | ||
|
|
cd343ba598 | ||
|
|
50069d3743 | ||
|
|
1e03f27f23 | ||
|
|
36bb55a9ce | ||
|
|
451e4050db | ||
|
|
367fd3e87f | ||
|
|
a67a2e12fd | ||
|
|
292978daf0 | ||
|
|
85a4a76a14 | ||
|
|
9d0ab7ed70 | ||
|
|
3d5b6a5e0b | ||
|
|
ab20372093 | ||
|
|
ab887f30e4 | ||
|
|
6cb6a8c25f | ||
|
|
9d1d2aca0a | ||
|
|
75cb2cd1f7 | ||
|
|
ed6adbbfd7 | ||
|
|
b6c950ffc5 | ||
|
|
8b89c69d5f | ||
|
|
290dda50fe | ||
|
|
a7278573a8 | ||
|
|
68ce4dc689 | ||
|
|
a9f5118013 | ||
|
|
d1f2e8ecf9 | ||
|
|
fe6873b61a | ||
|
|
7c7bcf80cf | ||
|
|
cf6022866d | ||
|
|
c630cb1de2 | ||
|
|
2e6c8d542c | ||
|
|
2f781906b5 | ||
|
|
b4f3b6f7bd | ||
|
|
71b464f44a | ||
|
|
438070ed58 | ||
|
|
a06c891969 | ||
|
|
a935bd09aa | ||
|
|
fb8745ccf0 | ||
|
|
53a4c4be7f | ||
|
|
c0a110ea8a | ||
|
|
c426a8e331 | ||
|
|
526e4f69a4 | ||
|
|
f901fa50ff | ||
|
|
bea9eeac16 | ||
|
|
3055da5316 | ||
|
|
3706fbdfee | ||
|
|
b4d18e94da | ||
|
|
41523d9555 | ||
|
|
3b4c025743 | ||
|
|
df065c5647 | ||
|
|
21380aacb8 | ||
|
|
5884b1cec5 | ||
|
|
05860fcf9b | ||
|
|
2916aaeda8 | ||
|
|
ec9a221a8f | ||
|
|
97d8ad7fc6 | ||
|
|
adfb4cd694 | ||
|
|
34374afd36 | ||
|
|
b29949c0fa | ||
|
|
d26e163f66 | ||
|
|
e47fb67d74 | ||
|
|
b328c49899 | ||
|
|
9f3b602c0a | ||
|
|
994a68eb81 | ||
|
|
bc256a989e | ||
|
|
2aeec2d50f | ||
|
|
03afa7652b | ||
|
|
6791cf1e2e | ||
|
|
e69b90ef1a | ||
|
|
6c925a32b4 | ||
|
|
8d3f381a5a | ||
|
|
02c424bd21 | ||
|
|
c324ee310a | ||
|
|
fe7250bfc1 | ||
|
|
63c1b046e0 | ||
|
|
ed98ae3e70 | ||
|
|
2011defb1e | ||
|
|
b35737b5d7 | ||
|
|
aeaf1632c8 | ||
|
|
af70573634 |
41
.clang-format
Normal file
41
.clang-format
Normal file
@@ -0,0 +1,41 @@
|
||||
BasedOnStyle: llvm
|
||||
---
|
||||
AccessModifierOffset: -4
|
||||
AlignEscapedNewlines: DontAlign
|
||||
AllowShortBlocksOnASingleLine: Empty
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
BreakBeforeTernaryOperators: true
|
||||
ColumnLimit: 0
|
||||
DerivePointerAlignment: false
|
||||
IncludeCategories:
|
||||
- Regex: '<[[:alnum:]_]+>'
|
||||
Priority: 1
|
||||
- Regex: '<(gtest|gmock)/'
|
||||
Priority: 2
|
||||
- Regex: '<[[:alnum:]_./]+>'
|
||||
Priority: 3
|
||||
- Regex: '<entt/'
|
||||
Priority: 4
|
||||
- Regex: '.*'
|
||||
Priority: 5
|
||||
IndentPPDirectives: AfterHash
|
||||
IndentWidth: 4
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
Language: Cpp
|
||||
PointerAlignment: Right
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceAroundPointerQualifiers: After
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCtorInitializerColon: false
|
||||
SpaceBeforeInheritanceColon: false
|
||||
SpaceBeforeParens: Never
|
||||
SpaceBeforeRangeBasedForLoopColon: false
|
||||
Standard: Latest
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# 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
|
||||
180
.github/workflows/build.yml
vendored
Normal file
180
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
name: build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
|
||||
linux:
|
||||
timeout-minutes: 10
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
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++
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install compiler
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install ${{ matrix.compiler.pkg }} -y
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
env:
|
||||
CXX: ${{ matrix.compiler.exe }}
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
windows:
|
||||
timeout-minutes: 10
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
toolset: [clang-cl, default, v141]
|
||||
include:
|
||||
- toolset: clang-cl
|
||||
toolset_option: -T"ClangCl"
|
||||
- toolset: v141
|
||||
toolset_option: -T"v141"
|
||||
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ${{ matrix.toolset_option }} ..
|
||||
cmake --build . -j 4
|
||||
- name: Run tests
|
||||
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
|
||||
|
||||
macos:
|
||||
timeout-minutes: 10
|
||||
runs-on: macOS-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
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
|
||||
|
||||
macos-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: macOS-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 }} ..
|
||||
make -j4
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
38
.github/workflows/coverage.yml
vendored
Normal file
38
.github/workflows/coverage.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: coverage
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
|
||||
codecov:
|
||||
timeout-minutes: 10
|
||||
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
|
||||
39
.github/workflows/deploy.yml
vendored
Normal file
39
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: deploy
|
||||
|
||||
on:
|
||||
release:
|
||||
types: published
|
||||
|
||||
jobs:
|
||||
|
||||
homebrew-entt:
|
||||
timeout-minutes: 5
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
GH_REPO: homebrew-entt
|
||||
FORMULA: entt.rb
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Clone repository
|
||||
working-directory: build
|
||||
env:
|
||||
PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
|
||||
run: git clone https://$GITHUB_ACTOR:$PERSONAL_ACCESS_TOKEN@github.com/$GITHUB_ACTOR/$GH_REPO.git
|
||||
- name: Prepare formula
|
||||
working-directory: build
|
||||
run: |
|
||||
cd $GH_REPO
|
||||
curl "https://github.com/${{ github.repository }}/archive/${{ github.ref }}.tar.gz" --location --fail --silent --show-error --output archive.tar.gz
|
||||
sed -i -e '/url/s/".*"/"'$(echo "https://github.com/${{ github.repository }}/archive/${{ github.ref }}.tar.gz" | sed -e 's/[\/&]/\\&/g')'"/' $FORMULA
|
||||
sed -i -e '/sha256/s/".*"/"'$(openssl sha256 archive.tar.gz | cut -d " " -f 2)'"/' $FORMULA
|
||||
- name: Update remote
|
||||
working-directory: build
|
||||
run: |
|
||||
cd $GH_REPO
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "$GITHUB_ACTOR"
|
||||
git add $FORMULA
|
||||
git commit -m "Update to ${{ github.ref }}"
|
||||
git push origin master
|
||||
36
.github/workflows/sanitizer.yml
vendored
Normal file
36
.github/workflows/sanitizer.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: sanitizer
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
|
||||
linux:
|
||||
timeout-minutes: 10
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
compiler: [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_USE_SANITIZER=ON -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=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
|
||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -1,2 +1,13 @@
|
||||
# QtCreator
|
||||
# Conan
|
||||
conan/test_package/build
|
||||
|
||||
# IDEs
|
||||
*.user
|
||||
.idea
|
||||
.vscode
|
||||
.vs
|
||||
CMakeSettings.json
|
||||
cpp.hint
|
||||
|
||||
# Bazel
|
||||
/bazel-*
|
||||
|
||||
55
.travis.yml
55
.travis.yml
@@ -1,55 +0,0 @@
|
||||
language: cpp
|
||||
dist: trusty
|
||||
sudo: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-6']
|
||||
env: COMPILER=g++-6
|
||||
- os: linux
|
||||
compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-4.0']
|
||||
packages: ['clang-4.0', 'libstdc++-4.9-dev']
|
||||
env: COMPILER=clang++-4.0
|
||||
- os: osx
|
||||
osx_image: xcode8.3
|
||||
compiler: clang
|
||||
env: COMPILER=clang++
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-6']
|
||||
env:
|
||||
- COMPILER=g++-6
|
||||
- CXXFLAGS="-O0 --coverage -fno-inline -fno-inline-small-functions -fno-default-inline"
|
||||
before_script:
|
||||
- pip install --user cpp-coveralls
|
||||
after_success:
|
||||
- coveralls --gcov gcov-6 --gcov-options '\-lp' --root ${TRAVIS_BUILD_DIR} --build-root ${TRAVIS_BUILD_DIR}/build --extension cpp --extension hpp --exclude deps --include src
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
on_failure: always
|
||||
|
||||
install:
|
||||
- echo ${PATH}
|
||||
- cmake --version
|
||||
- export CXX=${COMPILER}
|
||||
- echo ${CXX}
|
||||
- ${CXX} --version
|
||||
- ${CXX} -v
|
||||
|
||||
script:
|
||||
- mkdir -p build && cd build
|
||||
- cmake -DCMAKE_BUILD_TYPE=Release .. && make -j4
|
||||
- CTEST_OUTPUT_ON_FAILURE=1 make test
|
||||
48
AUTHORS
48
AUTHORS
@@ -1,7 +1,51 @@
|
||||
# Author
|
||||
|
||||
Michele Caini aka skypjack
|
||||
skypjack
|
||||
|
||||
# Contributors
|
||||
|
||||
Paolo Monteverde aka morbo84
|
||||
alexames
|
||||
BenediktConze
|
||||
bjadamson
|
||||
ceeac
|
||||
ColinH
|
||||
corystegel
|
||||
Croydon
|
||||
cugone
|
||||
dbacchet
|
||||
dBagrat
|
||||
djarek
|
||||
DonKult
|
||||
drglove
|
||||
eliasdaler
|
||||
erez-o
|
||||
eugeneko
|
||||
gale83
|
||||
ghost
|
||||
grdowns
|
||||
Green-Sky
|
||||
Innokentiy-Alaytsev
|
||||
Kerndog73
|
||||
Koward
|
||||
Lawrencemm
|
||||
markand
|
||||
mhammerc
|
||||
Milerius
|
||||
morbo84
|
||||
m-waka
|
||||
netpoetica
|
||||
NixAJ
|
||||
Oortonaut
|
||||
Paolo-Oliverio
|
||||
pgruenbacher
|
||||
prowolf
|
||||
stefanofiorentino
|
||||
suVrik
|
||||
szunhammer
|
||||
The5-1
|
||||
vblanco20-1
|
||||
willtunnels
|
||||
WizardIke
|
||||
w1th0utnam3
|
||||
xissburg
|
||||
zaucy
|
||||
|
||||
14
BUILD.bazel
Normal file
14
BUILD.bazel
Normal file
@@ -0,0 +1,14 @@
|
||||
_msvc_copts = ["/std:c++17"]
|
||||
_gcc_copts = ["-std=c++17"]
|
||||
|
||||
cc_library(
|
||||
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,
|
||||
}),
|
||||
)
|
||||
327
CMakeLists.txt
327
CMakeLists.txt
@@ -2,7 +2,7 @@
|
||||
# EnTT
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
cmake_minimum_required(VERSION 3.12.4)
|
||||
|
||||
#
|
||||
# Building in-tree is not allowed (we take care of your craziness).
|
||||
@@ -12,69 +12,324 @@ 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 VERSION 1.0.0)
|
||||
project(
|
||||
EnTT
|
||||
VERSION ${ENTT_VERSION}
|
||||
DESCRIPTION "Gaming meets modern C++ - a fast and reliable entity-component system (ECS) and much more"
|
||||
HOMEPAGE_URL "https://github.com/skypjack/entt"
|
||||
LANGUAGES C CXX
|
||||
)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
endif()
|
||||
|
||||
set(SETTINGS_ORGANIZATION "Michele Caini")
|
||||
set(SETTINGS_APPLICATION ${PROJECT_NAME})
|
||||
set(PROJECT_AUTHOR "Michele Caini")
|
||||
set(PROJECT_AUTHOR_EMAIL "michele.caini@gmail.com")
|
||||
|
||||
message("*")
|
||||
message("* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
|
||||
message("* Copyright (c) 2017 ${PROJECT_AUTHOR} <${PROJECT_AUTHOR_EMAIL}>")
|
||||
message("*")
|
||||
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 "*")
|
||||
|
||||
#
|
||||
# Compile stuff
|
||||
# Compiler stuff
|
||||
#
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall -Wconversion")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g -DDEBUG")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -DRELEASE")
|
||||
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)
|
||||
|
||||
if(ENTT_USE_LIBCPP)
|
||||
if(NOT WIN32)
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(CMakePushCheckState)
|
||||
|
||||
cmake_push_check_state()
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -stdlib=libc++")
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include<type_traits>
|
||||
int main() { return std::is_same_v<int, char>; }
|
||||
" ENTT_HAS_LIBCPP)
|
||||
|
||||
cmake_pop_check_state()
|
||||
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.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENTT_USE_SANITIZER)
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
|
||||
set(ENTT_HAS_SANITIZER TRUE CACHE BOOL "" FORCE)
|
||||
mark_as_advanced(ENTT_HAS_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.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#
|
||||
# CMake configuration
|
||||
# Add EnTT target
|
||||
#
|
||||
|
||||
set(PROJECT_CMAKE_IN ${entt_SOURCE_DIR}/cmake/in)
|
||||
set(PROJECT_DEPS_DIR ${entt_SOURCE_DIR}/deps)
|
||||
set(PROJECT_SRC_DIR ${entt_SOURCE_DIR}/src)
|
||||
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)
|
||||
|
||||
set(PROJECT_RUNTIME_OUTPUT_DIRECTORY bin)
|
||||
if(ENTT_INCLUDE_NATVIS)
|
||||
if(MSVC)
|
||||
set(ENTT_HAS_NATVIS TRUE CACHE BOOL "" FORCE)
|
||||
mark_as_advanced(ENTT_HAS_NATVIS)
|
||||
endif()
|
||||
|
||||
if(NOT ENTT_HAS_NATVIS)
|
||||
message(VERBOSE "The option ENTT_INCLUDE_NATVIS is set but natvis files are not supported. They will not be added to the target.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
add_library(EnTT INTERFACE)
|
||||
add_library(EnTT::EnTT ALIAS EnTT)
|
||||
|
||||
target_include_directories(
|
||||
EnTT
|
||||
INTERFACE
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
)
|
||||
|
||||
target_compile_features(EnTT INTERFACE cxx_std_17)
|
||||
|
||||
if(ENTT_INCLUDE_HEADERS)
|
||||
target_sources(
|
||||
EnTT
|
||||
INTERFACE
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/config/config.h>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/config/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/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/algorithm.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/any.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/attribute.h>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/compressed_pair.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/enum.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/family.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/hashed_string.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/ident.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/iterator.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/memory.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/monostate.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/tuple.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/type_info.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/type_traits.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/utility.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/component.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/entity.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/group.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/handle.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/helper.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/observer.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/organizer.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/registry.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/runtime_view.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/snapshot.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/sparse_set.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/storage.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/utility.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/view.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/factory.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/meta.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/node.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/pointer.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/policy.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/range.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/resolve.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/template.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/type_traits.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/utility.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/platform/android-ndk-r17.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/poly/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/poly/poly.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/process/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/signal/delegate.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/dispatcher.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/emitter.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/sigh.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entt.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/fwd.hpp>
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ENTT_HAS_NATVIS)
|
||||
target_sources(
|
||||
EnTT
|
||||
INTERFACE
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/config.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/container.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/core.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/entity.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/locator.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/meta.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/platform.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/poly.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/process.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/resource.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/signal.natvis>
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ENTT_HAS_SANITIZER)
|
||||
target_compile_options(EnTT INTERFACE $<$<CONFIG:Debug>:-fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined>)
|
||||
target_link_libraries(EnTT INTERFACE $<$<CONFIG:Debug>:-fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined>)
|
||||
endif()
|
||||
|
||||
if(ENTT_HAS_LIBCPP)
|
||||
target_compile_options(EnTT BEFORE INTERFACE -stdlib=libc++)
|
||||
endif()
|
||||
|
||||
#
|
||||
# Enable test support using ctest-like interface
|
||||
# Install pkg-config file
|
||||
#
|
||||
|
||||
option(BUILD_TESTING "Enable testing with ctest." ON)
|
||||
set(EnTT_PKGCONFIG ${CMAKE_CURRENT_BINARY_DIR}/entt.pc)
|
||||
|
||||
configure_file(
|
||||
${EnTT_SOURCE_DIR}/cmake/in/entt.pc.in
|
||||
${EnTT_PKGCONFIG}
|
||||
@ONLY
|
||||
)
|
||||
|
||||
install(
|
||||
FILES ${EnTT_PKGCONFIG}
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
||||
)
|
||||
|
||||
#
|
||||
# build testing stuff if required
|
||||
# Install EnTT
|
||||
#
|
||||
|
||||
if(BUILD_TESTING)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
# gtest, gtest_main, gmock and gmock_main targets are available from now on
|
||||
set(GOOGLETEST_DEPS_DIR ${PROJECT_DEPS_DIR}/googletest)
|
||||
configure_file(${PROJECT_CMAKE_IN}/googletest.in ${GOOGLETEST_DEPS_DIR}/CMakeLists.txt)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${GOOGLETEST_DEPS_DIR})
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${GOOGLETEST_DEPS_DIR})
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
add_subdirectory(${GOOGLETEST_DEPS_DIR}/src ${GOOGLETEST_DEPS_DIR}/build)
|
||||
install(
|
||||
TARGETS EnTT
|
||||
EXPORT EnTTTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
|
||||
write_basic_package_version_file(
|
||||
EnTTConfigVersion.cmake
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
|
||||
configure_package_config_file(
|
||||
${EnTT_SOURCE_DIR}/cmake/in/EnTTConfig.cmake.in
|
||||
EnTTConfig.cmake
|
||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
|
||||
)
|
||||
|
||||
export(
|
||||
EXPORT EnTTTargets
|
||||
FILE ${CMAKE_CURRENT_BINARY_DIR}/EnTTTargets.cmake
|
||||
NAMESPACE EnTT::
|
||||
)
|
||||
|
||||
install(
|
||||
EXPORT EnTTTargets
|
||||
FILE EnTTTargets.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
|
||||
NAMESPACE EnTT::
|
||||
)
|
||||
|
||||
install(
|
||||
FILES
|
||||
${PROJECT_BINARY_DIR}/EnTTConfig.cmake
|
||||
${PROJECT_BINARY_DIR}/EnTTConfigVersion.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
|
||||
)
|
||||
|
||||
install(DIRECTORY src/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
||||
export(PACKAGE EnTT)
|
||||
|
||||
#
|
||||
# Tests
|
||||
#
|
||||
|
||||
option(ENTT_BUILD_TESTING "Enable building tests." OFF)
|
||||
|
||||
if(ENTT_BUILD_TESTING)
|
||||
option(ENTT_FIND_GTEST_PACKAGE "Enable finding gtest package." OFF)
|
||||
option(ENTT_BUILD_BENCHMARK "Build benchmark." OFF)
|
||||
option(ENTT_BUILD_EXAMPLE "Build examples." OFF)
|
||||
option(ENTT_BUILD_LIB "Build lib tests." OFF)
|
||||
option(ENTT_BUILD_SNAPSHOT "Build snapshot test with Cereal." OFF)
|
||||
|
||||
set(ENTT_ID_TYPE std::uint32_t CACHE STRING "Type of identifiers to use for the tests")
|
||||
set(ENTT_CXX_STD cxx_std_17 CACHE STRING "C++ standard revision to use for the tests")
|
||||
|
||||
include(CTest)
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
#
|
||||
# Documentation
|
||||
#
|
||||
|
||||
option(ENTT_BUILD_DOCS "Enable building with documentation." OFF)
|
||||
|
||||
if(ENTT_BUILD_DOCS)
|
||||
find_package(Doxygen 1.8)
|
||||
|
||||
if(DOXYGEN_FOUND)
|
||||
add_subdirectory(docs)
|
||||
endif()
|
||||
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
|
||||
)
|
||||
|
||||
43
CONTRIBUTING.md
Normal file
43
CONTRIBUTING.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Contributing
|
||||
|
||||
First of all, thank you very much for taking the time to contribute to the
|
||||
`EnTT` framework.<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
|
||||
you by searching on GitHub under
|
||||
[issues](https://github.com/skypjack/entt/issues). Do not forget to search
|
||||
also through the closed ones. If you are unable to find a proper answer, feel
|
||||
free to [open a new issue](https://github.com/skypjack/entt/issues/new).
|
||||
Usually, questions are marked as such and closed in a few days.
|
||||
|
||||
* If you want to fix a typo in the inline documentation or in the README file,
|
||||
if you want to add some new sections or if you want to help me with the
|
||||
language by reviewing what I wrote so far (I'm not a native speaker after
|
||||
all), **please** open a new
|
||||
[pull request](https://github.com/skypjack/entt/pulls) with your changes.
|
||||
|
||||
* If you found a bug, **please** ensure there isn't already an answer for you by
|
||||
searching on GitHub under [issues](https://github.com/skypjack/entt/issues).
|
||||
If you are unable to find an open issue addressing the problem, feel free to
|
||||
[open a new one](https://github.com/skypjack/entt/issues/new). **Please**, do
|
||||
not forget to carefully describe how to reproduce the problem, then add all
|
||||
the information about the system on which you are experiencing it and point
|
||||
out the version of `EnTT` you are using (tag or commit).
|
||||
|
||||
* 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
|
||||
[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.
|
||||
|
||||
* If you want to propose a new feature and you know how to code it, **please**
|
||||
do not issue directly a pull request. Before to do it,
|
||||
[create a new issue](https://github.com/skypjack/entt/issues/new) to discuss
|
||||
your proposal. Other users could be interested in your idea and the discussion
|
||||
that will follow can refine it and therefore give us a better solution.
|
||||
|
||||
* If you want to request a new feature, I'm available for hiring. Take a look at
|
||||
[my profile](https://github.com/skypjack) and feel free to write me.
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Michele Caini
|
||||
Copyright (c) 2017-2021 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
|
||||
|
||||
599
README.md
599
README.md
@@ -1,349 +1,428 @@
|
||||
# EnTT - Entity-Component System in modern C++
|
||||

|
||||
|
||||
[](https://travis-ci.org/skypjack/uvw)
|
||||
[](https://ci.appveyor.com/project/skypjack/entt)
|
||||
[](https://coveralls.io/github/skypjack/entt?branch=master)
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
[](https://github.com/skypjack/entt/actions)
|
||||
[](https://codecov.io/gh/skypjack/entt)
|
||||
[](https://godbolt.org/z/zxW73f)
|
||||
[](http://entt.docsforge.com/)
|
||||
[](https://gitter.im/skypjack/entt)
|
||||
[](https://discord.gg/5BjPWBd)
|
||||
[](https://www.paypal.me/skypjack)
|
||||
|
||||
`EnTT` is a header-only, tiny and easy to use library for game programming and
|
||||
much more written in **modern C++**.<br/>
|
||||
[Among others](https://github.com/skypjack/entt/wiki/EnTT-in-Action), it's used
|
||||
in [**Minecraft**](https://minecraft.net/en-us/attribution/) by Mojang, the
|
||||
[**ArcGIS Runtime SDKs**](https://developers.arcgis.com/arcgis-runtime/) by Esri
|
||||
and the amazing [**Ragdoll**](https://ragdolldynamics.com/).<br/>
|
||||
If you don't see your project in the list, please open an issue, submit a PR or
|
||||
add the [#entt](https://github.com/topics/entt) tag to your _topics_! :+1:
|
||||
|
||||
---
|
||||
|
||||
Do you want to **keep up with changes** or do you have a **question** that
|
||||
doesn't require you to open an issue?<br/>
|
||||
Join the [gitter channel](https://gitter.im/skypjack/entt) and the
|
||||
[discord server](https://discord.gg/5BjPWBd), meet other users like you. The
|
||||
more we are, the better for everyone.<br/>
|
||||
Don't forget to check the
|
||||
[FAQs](https://github.com/skypjack/entt/wiki/Frequently-Asked-Questions) and the
|
||||
[wiki](https://github.com/skypjack/entt/wiki) too. Your answers may already be
|
||||
there.
|
||||
|
||||
Do you want to support `EnTT`? Consider becoming a
|
||||
[**sponsor**](https://github.com/users/skypjack/sponsorship).
|
||||
Many thanks to [these people](https://skypjack.github.io/sponsorship/) and
|
||||
**special** thanks to:
|
||||
|
||||
[](https://mojang.com)
|
||||
[](https://img.ly/)
|
||||
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Code Example](#code-example)
|
||||
* [Motivation](#motivation)
|
||||
* [Performance](#performance)
|
||||
* [Integration](#integration)
|
||||
* [Requirements](#requirements)
|
||||
* [CMake](#cmake)
|
||||
* [Natvis support](#natvis-support)
|
||||
* [Packaging Tools](#packaging-tools)
|
||||
* [pkg-config](#pkg-config)
|
||||
* [Documentation](#documentation)
|
||||
* [Tests](#tests)
|
||||
* [EnTT in Action](#entt-in-action)
|
||||
* [Contributors](#contributors)
|
||||
* [License](#license)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
`EnTT` is a header-only, tiny and easy to use Entity-Component System in modern C++.<br/>
|
||||
ECS is an architectural pattern used mostly in game development. For further details:
|
||||
The entity-component-system (also known as _ECS_) is an architectural pattern
|
||||
used mostly in game development. For further details:
|
||||
|
||||
* [Entity Systems Wiki](http://entity-systems.wikidot.com/)
|
||||
* [Evolve Your Hierarchy](http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/)
|
||||
* [ECS on Wikipedia](https://en.wikipedia.org/wiki/Entity%E2%80%93component%E2%80%93system)
|
||||
|
||||
This project started off as a pure entity-component system. Over time the
|
||||
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**.
|
||||
* Minimal **configuration system** built using the monostate pattern.
|
||||
* Incredibly fast **entity-component system** with its own _pay for what you
|
||||
use_ policy.
|
||||
* 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.
|
||||
* 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 **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.
|
||||
* A general purpose **event emitter** as a CRTP idiom based class template.
|
||||
* And **much more**! Check out the
|
||||
[**wiki**](https://github.com/skypjack/entt/wiki).
|
||||
|
||||
**Breaking news**:
|
||||
|
||||
* 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/>
|
||||
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.
|
||||
|
||||
## Code Example
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <registry.hpp>
|
||||
#include <entt/entt.hpp>
|
||||
|
||||
struct Position {
|
||||
struct position {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
struct Velocity {
|
||||
struct velocity {
|
||||
float dx;
|
||||
float dy;
|
||||
};
|
||||
|
||||
using ECS = entt::DefaultRegistry<Position, Velocity>;
|
||||
void update(entt::registry ®istry) {
|
||||
auto view = registry.view<const position, velocity>();
|
||||
|
||||
// use a callback
|
||||
view.each([](const auto &pos, auto &vel) { /* ... */ });
|
||||
|
||||
// use an extended callback
|
||||
view.each([](const auto entity, const auto &pos, auto &vel) { /* ... */ });
|
||||
|
||||
// use a range-for
|
||||
for(auto [entity, pos, vel]: view.each()) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// use forward iterators and get only the components of interest
|
||||
for(auto entity: view) {
|
||||
auto &vel = view.get<velocity>(entity);
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
ECS ecs;
|
||||
entt::registry registry;
|
||||
|
||||
for(auto i = 0; i < 10; ++i) {
|
||||
auto entity = ecs.create();
|
||||
ecs.assign<Position>(entity, i * 1.f, i * 1.f);
|
||||
if(i % 2 == 0) { ecs.assign<Velocity>(entity, i * .1f, i * .1f); }
|
||||
for(auto i = 0u; i < 10u; ++i) {
|
||||
const auto entity = registry.create();
|
||||
registry.emplace<position>(entity, i * 1.f, i * 1.f);
|
||||
if(i % 2 == 0) { registry.emplace<velocity>(entity, i * .1f, i * .1f); }
|
||||
}
|
||||
|
||||
std::cout << "single component view" << std::endl;
|
||||
|
||||
for(auto entity: ecs.view<Position>()) {
|
||||
auto &position = ecs.get<Position>(entity);
|
||||
std::cout << position.x << "," << position.y << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "multi component view" << std::endl;
|
||||
|
||||
for(auto entity: ecs.view<Position, Velocity>()) {
|
||||
auto &position = ecs.get<Position>(entity);
|
||||
auto &velocity = ecs.get<Velocity>(entity);
|
||||
std::cout << position.x << "," << position.y << " - " << velocity.dx << "," << velocity.dy << std::endl;
|
||||
if(entity % 4) { ecs.remove<Velocity>(entity); }
|
||||
else { ecs.destroy(entity); }
|
||||
}
|
||||
|
||||
std::cout << "single component view" << std::endl;
|
||||
|
||||
for(auto entity: ecs.view<Position>()) {
|
||||
auto &position = ecs.get<Position>(entity);
|
||||
std::cout << position.x << "," << position.y << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "multi component view" << std::endl;
|
||||
|
||||
for(auto entity: ecs.view<Position, Velocity>()) {
|
||||
auto &position = ecs.get<Position>(entity);
|
||||
auto &velocity = ecs.get<Velocity>(entity);
|
||||
std::cout << position.x << "," << position.y << " - " << velocity.dx << "," << velocity.dy << std::endl;
|
||||
if(entity % 4) { ecs.remove<Velocity>(entity); }
|
||||
else { ecs.destroy(entity); }
|
||||
}
|
||||
|
||||
ecs.reset();
|
||||
update(registry);
|
||||
}
|
||||
```
|
||||
|
||||
## Motivation
|
||||
|
||||
I started using another well known Entity-Component System named [`EntityX`](https://github.com/alecthomas/entityx).<br/>
|
||||
While I was playing with it, I found that I didn't like that much the way it manages its memory.
|
||||
Moreover, I was pretty sure that one could achieve better performance with a slightly modified pool under the hood.<br/>
|
||||
That's also the reason for which the interface is quite similar to the one of `EntityX`, so that `EnTT` can be used as a
|
||||
drop-in replacement for it with a minimal effort.
|
||||
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
|
||||
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
|
||||
realized it, I tried hard to keep intact the great performance of `EnTT` and to
|
||||
add all the features I wanted to see in *my own library* at the same time.
|
||||
|
||||
### Performance
|
||||
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.
|
||||
|
||||
As it stands right now, `EnTT` is just fast enough for my requirements if compared to my first choice (that was already
|
||||
amazingly fast).
|
||||
These are the results of the twos when compiled with GCC 6.3:
|
||||
## Performance
|
||||
|
||||
| Benchmark | EntityX (experimental/compile_time) | EnTT |
|
||||
|-----------|-------------|-------------|
|
||||
| Creating 10M entities | 0.187042s | **0.0928331s** |
|
||||
| Destroying 10M entities | 0.0735151s | **0.060166s** |
|
||||
| Iterating over 10M entities, unpacking one component | 0.00784801s | **1.02e-07s** |
|
||||
| Iterating over 10M entities, unpacking two components | 0.00865273s | **0.00326714s** |
|
||||
| Iterating over 10M entities, unpacking five components | 0.0122006s | **0.00323354s** |
|
||||
| Iterating over 10M entities, unpacking ten components | 0.0100089s | **0.00323615s** |
|
||||
| Iterating over 50M entities, unpacking one component | 0.0394404s | **1.14e-07s** |
|
||||
| Iterating over 50M entities, unpacking two components | 0.0400407s | **0.0179783s** |
|
||||
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.
|
||||
|
||||
These are the results of the twos when compiled with Clang 3.8.1:
|
||||
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.
|
||||
|
||||
| Benchmark | EntityX (experimental/compile_time) | EnTT |
|
||||
|-----------|-------------|-------------|
|
||||
| Creating 10M entities | 0.268049s | **0.0899998s** |
|
||||
| Destroying 10M entities | **0.0713912s** | 0.078663s |
|
||||
| Iterating over 10M entities, unpacking one component | 0.00863192s | **3.05e-07s** |
|
||||
| Iterating over 10M entities, unpacking two components | 0.00780158s | **2.5434e-05s** |
|
||||
| Iterating over 10M entities, unpacking five components | 0.00829669s | **2.5497e-05s** |
|
||||
| Iterating over 10M entities, unpacking ten components | 0.00789789s | **2.5563e-05s** |
|
||||
| Iterating over 50M entities, unpacking one component | 0.0423244s | **1.94e-07s** |
|
||||
| Iterating over 50M entities, unpacking two components | 0.0435464s | **0.00012661s** |
|
||||
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
|
||||
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.
|
||||
|
||||
I don't know what Clang does to squeeze out of `EnTT` the performance above, but I'd say that it does it incredibly well.
|
||||
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.
|
||||
|
||||
See [benchmark.cpp](https://github.com/skypjack/entt/blob/master/test/benchmark.cpp) for further details.<br/>
|
||||
Of course, I'll try to get out of it more features and better performance anyway in the future, mainly for fun.
|
||||
If you want to contribute and have any suggestion, feel free to make a PR or open an issue to discuss them.
|
||||
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.
|
||||
|
||||
### Benchmarks / Comparisons
|
||||
# Integration
|
||||
|
||||
`EnTT` includes its own benchmarks, mostly similar to the ones of `EntityX` so as to compare them.<br/>
|
||||
On Github you can find also a [benchmark suite](https://github.com/abeimler/ecs_benchmark) testing `EntityX` (both the official version and the compile-time one), `Anax` and `Artemis C++` with up to 10M entities.
|
||||
`EnTT` is a header-only library. This means that including the `entt.hpp` header
|
||||
is enough to include the library as a whole and use it. For those who are
|
||||
interested only in the entity-component system, consider to include the sole
|
||||
`entity/registry.hpp` header instead.<br/>
|
||||
It's a matter of adding the following line to the top of a file:
|
||||
|
||||
# Build Instructions
|
||||
```cpp
|
||||
#include <entt/entt.hpp>
|
||||
```
|
||||
|
||||
Use the line below to include only the entity-component system instead:
|
||||
|
||||
```cpp
|
||||
#include <entt/entity/registry.hpp>
|
||||
```
|
||||
|
||||
Then pass the proper `-I` argument to the compiler to add the `src` directory to
|
||||
the include paths.
|
||||
|
||||
## Requirements
|
||||
|
||||
To be able to use `EnTT`, users must provide a full-featured compiler that supports at least C++14.<br/>
|
||||
CMake version 3.4 or later is mandatory to compile the tests, you don't have to install it otherwise.
|
||||
To be able to use `EnTT`, users must provide a full-featured compiler that
|
||||
supports at least C++17.<br/>
|
||||
The requirements below are mandatory to compile the tests and to extract the
|
||||
documentation:
|
||||
|
||||
## Library
|
||||
* `CMake` version 3.7 or later.
|
||||
* `Doxygen` version 1.8 or later.
|
||||
|
||||
`EnTT` is a header-only library. This means that including the `registry.hpp` header is enough to use it.<br/>
|
||||
It's a matter of adding the following line at the top of a file:
|
||||
Alternatively, [Bazel](https://bazel.build) is also supported as a build system
|
||||
(credits to [zaucy](https://github.com/zaucy) who offered to maintain it).<br/>
|
||||
In the documentation below I'll still refer to `CMake`, this being the official
|
||||
build system of the library.
|
||||
|
||||
```cpp
|
||||
#include <registry.hpp>
|
||||
```
|
||||
## CMake
|
||||
|
||||
Then pass the proper `-I` argument to the compiler to add the `src` directory to the include paths.<br/>
|
||||
To use `EnTT` from a `CMake` project, just link an existing target to the
|
||||
`EnTT::EnTT` alias.<br/>
|
||||
The library offers everything you need for locating (as in `find_package`),
|
||||
embedding (as in `add_subdirectory`), fetching (as in `FetchContent`) or using
|
||||
it in many of the ways that you can think of and that involve `CMake`.<br/>
|
||||
Covering all possible cases would require a treaty and not a simple README file,
|
||||
but I'm confident that anyone reading this section also knows what it's about
|
||||
and can use `EnTT` from a `CMake` project without problems.
|
||||
|
||||
## Documentation
|
||||
## Natvis support
|
||||
|
||||
### API Reference
|
||||
When using `CMake`, just enable the option `ENTT_INCLUDE_NATVIS` and enjoy
|
||||
it.<br/>
|
||||
Otherwise, most of the tools are covered via Natvis and all files can be found
|
||||
in the `natvis` directory, divided by module.<br/>
|
||||
If you spot errors or have suggestions, any contribution is welcome!
|
||||
|
||||
`EnTT` contains three main actors: the *registry*, the *view* and the *pool*.<br/>
|
||||
Unless you have specific requirements of memory management, the default registry (that used the pool provided with
|
||||
`EnTT`) should be good enough for any use. Customization is an option anyway, so that you can use your own pool as
|
||||
long as it offers the expected interface.
|
||||
## Packaging Tools
|
||||
|
||||
#### The Registry
|
||||
`EnTT` is available for some of the most known packaging tools. In particular:
|
||||
|
||||
There are three options to instantiate your own registry:
|
||||
* [`Conan`](https://github.com/conan-io/conan-center-index), the C/C++ Package
|
||||
Manager for Developers.
|
||||
|
||||
* By using the default one:
|
||||
* [`vcpkg`](https://github.com/Microsoft/vcpkg), Microsoft VC++ Packaging
|
||||
Tool.<br/>
|
||||
You can download and install `EnTT` in just a few simple steps:
|
||||
|
||||
```cpp
|
||||
auto registry = entt::DefaultRegistry<Components...>{args...};
|
||||
```
|
||||
```
|
||||
$ git clone https://github.com/Microsoft/vcpkg.git
|
||||
$ cd vcpkg
|
||||
$ ./bootstrap-vcpkg.sh
|
||||
$ ./vcpkg integrate install
|
||||
$ vcpkg install entt
|
||||
```
|
||||
|
||||
That is, you must provide the whole list of components to be registered with the default registry.
|
||||
Or you can use the `experimental` feature to test the latest changes:
|
||||
|
||||
* By using the standard one:
|
||||
```
|
||||
vcpkg install entt[experimental] --head
|
||||
```
|
||||
|
||||
```cpp
|
||||
auto registry = entt::StandardRegistry<std::uint16_t, Components...>{args...};
|
||||
```
|
||||
The `EnTT` port in `vcpkg` is kept up to date by Microsoft team members and
|
||||
community contributors.<br/>
|
||||
If the version is out of date, please
|
||||
[create an issue or pull request](https://github.com/Microsoft/vcpkg) on the
|
||||
`vcpkg` repository.
|
||||
|
||||
That is, you must provide the whole list of components to be registered with the default registry **and** the desired type for the entities. Note that the default type is `std::uint32_t`, that is larger enough for almost all the games but also too big for the most of the games.
|
||||
* [`Homebrew`](https://github.com/skypjack/homebrew-entt), the missing package
|
||||
manager for macOS.<br/>
|
||||
Available as a homebrew formula. Just type the following to install it:
|
||||
|
||||
* By using your own pool:
|
||||
```
|
||||
brew install skypjack/entt/entt
|
||||
```
|
||||
|
||||
```cpp
|
||||
auto registry = entt::Registry<DesiredEntityType, YourOwnPool<Components...>>{args...};
|
||||
```
|
||||
* [`build2`](https://build2.org), build toolchain for developing and packaging C
|
||||
and C++ code.<br/>
|
||||
In order to use the [`entt`](https://cppget.org/entt) package in a `build2`
|
||||
project, add the following line or a similar one to the `manifest` file:
|
||||
|
||||
Note that the registry expects a class template where the template parameters are the components to be managed.
|
||||
```
|
||||
depends: entt ^3.0.0
|
||||
```
|
||||
|
||||
In both cases, `args...` parameters are forwarded to the underlying pool during the construction.<br/>
|
||||
There are no requirements for the components but to be moveable, therefore POD types are just fine.
|
||||
Also check that the configuration refers to a valid repository, so that the
|
||||
package can be found by `build2`:
|
||||
|
||||
Once you have created a registry, the followings are the exposed member functions:
|
||||
* [`cppget.org`](https://cppget.org), the open-source community central
|
||||
repository, accessible as `https://pkg.cppget.org/1/stable`.
|
||||
|
||||
* `size`: returns the number of entities still alive.
|
||||
* `capacity`: returns the maximum number of entities created till now.
|
||||
* `valid`: returns true if the entity is still in use, false otherwise.
|
||||
* `empty<Component>`: returns `true` if at least an instance of `Component` exists, `false` otherwise.
|
||||
* `empty`: returns `true` if all the entities have been destroyed, `false` otherwise.
|
||||
* `create<Components...>`: creates a new entity and assigns it the given components, then returns the entity.
|
||||
* `create`: creates a new entity and returns it, no components assigned.
|
||||
* `destroy`: destroys the entity and all its components.
|
||||
* `assign<Component>(entity, args...)`: assigns the given component to the entity and uses `args...` to initialize it.
|
||||
* `remove<Component>(entity)`: removes the given component from the entity.
|
||||
* `has<Components...>(entity)`: returns `true` if the entity has the given components, `false` otherwise.
|
||||
* `get<Component>(entity)`: returns a reference to the given component for the entity (undefined behaviour if the entity has not the component).
|
||||
* `replace<Component>(entity, args...)`: replaces the given component for the entity, using `args...` to create the new component.
|
||||
* `accomodate<Component>(entity, args...)`: replaces the given component for the entity if it exists, otherwise assigns it to the entity and uses `args...` to initialize it.
|
||||
* `clone(entity)`: clones an entity and all its components, then returns the new entity identifier.
|
||||
* `copy<Component>(to, from)`: copies a component from an entity to another one (both the entities must already have been assigned the component, undefined behaviour otherwise).
|
||||
* `copy(to, from)`: copies all the components and their contents from an entity to another one (comoonents are created or destroyed if needed).
|
||||
* `reset<Component>(entity)`: removes the given component from the entity if assigned.
|
||||
* `reset<Component>()`: destroys all the instances of `Component`.
|
||||
* `reset()`: resets the pool and destroys all the entities and their components.
|
||||
* `view<Components...>()`: gets a view of the entities that have the given components (see below for further details).
|
||||
* [Package source repository](https://github.com/build2-packaging/entt):
|
||||
accessible as either `https://github.com/build2-packaging/entt.git` or
|
||||
`ssh://git@github.com/build2-packaging/entt.git`.
|
||||
Feel free to [report issues](https://github.com/build2-packaging/entt) with
|
||||
this package.
|
||||
|
||||
Note that entities are numbers and nothing more. They are not classes and they have no member functions at all.
|
||||
Both can be used with `bpkg add-repo` or added in a project
|
||||
`repositories.manifest`. See the official
|
||||
[documentation](https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml#guide-repositories)
|
||||
for more details.
|
||||
|
||||
#### The View
|
||||
Consider this list a work in progress and help me to make it longer if you like.
|
||||
|
||||
There are three different kinds of view, each one with a slighlty different interface:
|
||||
## pkg-config
|
||||
|
||||
* The _single component view_.
|
||||
* The _multi component view_.
|
||||
`EnTT` also supports `pkg-config` (for some definition of _supports_ at least).
|
||||
A suitable file called `entt.pc` is generated and installed in a proper
|
||||
directory when running `CMake`.<br/>
|
||||
This should also make it easier to use with tools such as `Meson` or similar.
|
||||
|
||||
All of them are iterable. In other terms they have `begin` and `end` member functions that are suitable for a range-based for loop:
|
||||
# Documentation
|
||||
|
||||
```cpp
|
||||
auto view = registry.view<Position, Velocity>();
|
||||
The documentation is based on [doxygen](http://www.doxygen.nl/). To build it:
|
||||
|
||||
for(auto entity: view) {
|
||||
// do whatever you want with your entities
|
||||
}
|
||||
```
|
||||
$ cd build
|
||||
$ cmake .. -DENTT_BUILD_DOCS=ON
|
||||
$ make
|
||||
|
||||
Iterators are extremely poor, they are meant exclusively to be used to iterate over a set of entities.<br/>
|
||||
Guaranteed exposed member functions are:
|
||||
The API reference will be created in HTML format within the directory
|
||||
`build/docs/html`. To navigate it with your favorite browser:
|
||||
|
||||
* `operator++()`
|
||||
* `operator++(int)`
|
||||
* `operator==()`
|
||||
* `operator!=()`
|
||||
* `operator*()`
|
||||
$ cd build
|
||||
$ your_favorite_browser docs/html/index.html
|
||||
|
||||
The single component view has an additional member function:
|
||||
<!--
|
||||
@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/>
|
||||
Moreover, there exists a [wiki](https://github.com/skypjack/entt/wiki) dedicated
|
||||
to the project where users can find all related documentation pages.
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
* `size()`: returns the exact number of expected entities.
|
||||
|
||||
The multi component view has an additional member function:
|
||||
|
||||
* `reset()`: reorganizes internal data so as to further create optimized iterators (use it whenever the data within the registry are known to be changed).
|
||||
|
||||
All the views can be used more than once. They return newly created and correctly initialized iterators whenever
|
||||
`begin` or `end` is invoked. Anyway views and iterators are tiny objects and the time to construct them can be safely ignored.
|
||||
I'd suggest not to store them anywhere and to invoke the `Registry::view` member function at each iteration to get a properly
|
||||
initialized view over which to iterate.
|
||||
|
||||
**Note**: If underlying sets are modified, iterators are invalidated and using them is undefined behaviour.<br/>
|
||||
Do not try to assign or remove components on which you are iterating to entities. There are no guarantees.
|
||||
|
||||
**Note**: Iterators aren't thread safe. Do no try to iterate over a set of components and modify them concurrently.
|
||||
That being said, as long as a thread iterates over the entities that have the component `X` or assign and removes
|
||||
that component from a set of entities and another thread does something similar with components `Y` and `Z`, it shouldn't be a
|
||||
problem at all.<br/>
|
||||
As an example, that means that users can freely run the rendering system over the renderable entities and update the physics
|
||||
concurrently on a separate thread if needed.
|
||||
|
||||
#### The Pool
|
||||
|
||||
Custom pools for a given component can be defined as a specialization of the class template `ComponentPool`.<br/>
|
||||
In particular:
|
||||
|
||||
```cpp
|
||||
template<>
|
||||
struct ComponentPool<Entity, MyComponent> final {
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
Where `Entity` is the desired type for the entities, `MyComponent` the type of the component to be stored.
|
||||
|
||||
A custom pool should expose at least the following member functions:
|
||||
|
||||
* `bool empty() const noexcept;`
|
||||
* `size_type capacity() const noexcept;`
|
||||
* `size_type size() const noexcept;`
|
||||
* `iterator_type begin() noexcept;`
|
||||
* `const_iterator_type begin() const noexcept;`
|
||||
* `iterator_type end() noexcept;`
|
||||
* `const_iterator_type end() const noexcept;`
|
||||
* `bool has(entity_type entity) const noexcept;`
|
||||
* `const component_type & get(entity_type entity) const noexcept;`
|
||||
* `component_type & get(entity_type entity) noexcept;`
|
||||
* `template<typename... Args> component_type & construct(entity_type entity, Args... args);`
|
||||
* `void destroy(entity_type entity);`
|
||||
* `void reset();`
|
||||
|
||||
This is a fast and easy way to define a custom pool specialization for a given component (as an example, if the
|
||||
component `X` requires to be ordered internally somehow during construction or destruction operations) and to use the
|
||||
default pool for all the other components.<br/>
|
||||
It's a mattrer of including the given specialization along with the registry, so that it can find it during the instantiation.<br/>
|
||||
In this case, users are not required to use the more explicit `Registry` class. Instead, they can still use `entt::DefaultRegistry`.
|
||||
|
||||
In cases when the per-component pools are not good enough, the registry can be initialized with a custom pool.<br/>
|
||||
In other terms, `entt::Registry` has a template template parameter that can be used to provide both the pool and the list of
|
||||
components:
|
||||
|
||||
```cpp
|
||||
auto registry = entt::Registry<Entity, MyCustomPool<Component1, Component2>>{};
|
||||
```
|
||||
|
||||
Even though the underlying pool doesn't store the components separately, the registry must know them to be able to do
|
||||
specific actions (like `destroy` or `copy`). That's why they must be explicitly specified.<br/>
|
||||
A generic pool should expose at least the following memeber functions:
|
||||
|
||||
* `template<typename Component> bool empty() const noexcept;`
|
||||
* `template<typename Component> size_type capacity() const noexcept;`
|
||||
* `template<typename Component> size_type size() const noexcept;`
|
||||
* `template<typename Component> iterator_type begin() noexcept;`
|
||||
* `template<typename Component> const_iterator_type begin() const noexcept;`
|
||||
* `template<typename Component> iterator_type end() noexcept;`
|
||||
* `template<typename Component> const_iterator_type end() const noexcept;`
|
||||
* `template<typename Component> bool has(entity_type entity) const noexcept;`
|
||||
* `template<typename Component> const Comp & get(entity_type entity) const noexcept;`
|
||||
* `template<typename Component> Comp & get(entity_type entity) noexcept;`
|
||||
* `template<typename Component, typename... Args> Comp & construct(entity_type entity, Args... args);`
|
||||
* `template<typename Component> void destroy(entity_type entity);`
|
||||
* `template<typename Component> void reset();`
|
||||
* `void reset();`
|
||||
|
||||
Good luck. If you come out with a more performant components pool, do not forget to make a PR so that I can add it to
|
||||
the list of available ones. I would be glad to have such a contribution to the project!!
|
||||
|
||||
## Tests
|
||||
# Tests
|
||||
|
||||
To compile and run the tests, `EnTT` requires *googletest*.<br/>
|
||||
Run the script `deps.sh` to download it. It is good practice to do it every time one pull the project.
|
||||
`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`.
|
||||
|
||||
Then, to build the tests:
|
||||
To build the most basic set of tests:
|
||||
|
||||
* `$ cd build`
|
||||
* `$ cmake ..`
|
||||
* `$ cmake -DENTT_BUILD_TESTING=ON ..`
|
||||
* `$ make`
|
||||
* `$ make test`
|
||||
|
||||
Note that benchmarks are not part of this set.
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# EnTT in Action
|
||||
|
||||
`EnTT` is widely used in private and commercial applications. I cannot even
|
||||
mention most of them because of some signatures I put on some documents time
|
||||
ago. Fortunately, there are also people who took the time to implement open
|
||||
source projects based on `EnTT` and did not hold back when it came to
|
||||
documenting them.
|
||||
|
||||
[Here](https://github.com/skypjack/entt/wiki/EnTT-in-Action) you can find an
|
||||
incomplete list of games, applications and articles that can be used as a
|
||||
reference.
|
||||
|
||||
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 the list.
|
||||
|
||||
# Contributors
|
||||
|
||||
If you want to contribute, please send patches as pull requests against the branch master.<br/>
|
||||
Check the [contributors list](https://github.com/skypjack/entt/blob/master/AUTHORS) to see who has partecipated so far.
|
||||
Requests for features, PRs, suggestions ad feedback are highly appreciated.
|
||||
|
||||
If you find you can help and want to contribute to the project with your
|
||||
experience or you do want to get part of the project for some other reason, feel
|
||||
free to contact me directly (you can find the mail in the
|
||||
[profile](https://github.com/skypjack)).<br/>
|
||||
I can't promise that each and every contribution will be accepted, but I can
|
||||
assure that I'll do my best to take them all as soon as possible.
|
||||
|
||||
If you decide to participate, please see the guidelines for
|
||||
[contributing](CONTRIBUTING.md) before to create issues or pull
|
||||
requests.<br/>
|
||||
Take also a look at the
|
||||
[contributors list](https://github.com/skypjack/entt/blob/master/AUTHORS) to
|
||||
know who has participated so far.
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# License
|
||||
|
||||
Code and documentation Copyright (c) 2017 Michele Caini.<br/>
|
||||
Code released under [the MIT license](https://github.com/skypjack/entt/blob/master/LICENSE).
|
||||
Code and documentation Copyright (c) 2017-2021 Michele Caini.<br/>
|
||||
Colorful logo Copyright (c) 2018-2021 Richard Caseres.
|
||||
|
||||
Code released under
|
||||
[the MIT license](https://github.com/skypjack/entt/blob/master/LICENSE).<br/>
|
||||
Documentation released under
|
||||
[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).<br/>
|
||||
All logos released under
|
||||
[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/).
|
||||
|
||||
25
TODO
Normal file
25
TODO
Normal file
@@ -0,0 +1,25 @@
|
||||
* 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 :)
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
* deprecate non-owning groups in favor of owning views and view packs, introduce lazy owning views
|
||||
* snapshot: support for range-based archives
|
||||
23
appveyor.yml
23
appveyor.yml
@@ -1,23 +0,0 @@
|
||||
# can use variables like {build} and {branch}
|
||||
version: 1.0.{build}
|
||||
|
||||
image: Visual Studio 2015
|
||||
|
||||
environment:
|
||||
BUILD_DIR: "%APPVEYOR_BUILD_FOLDER%\\build"
|
||||
|
||||
platform:
|
||||
- Win32
|
||||
|
||||
configuration:
|
||||
- Release
|
||||
|
||||
before_build:
|
||||
- deps.bat
|
||||
- cd %BUILD_DIR%
|
||||
- cmake .. -G"%CMAKE_GENERATOR_NAME%"
|
||||
|
||||
build:
|
||||
parallel: true
|
||||
project: build/entt.sln
|
||||
verbosity: minimal
|
||||
5
cmake/in/EnTTConfig.cmake.in
Normal file
5
cmake/in/EnTTConfig.cmake.in
Normal file
@@ -0,0 +1,5 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
set(EnTT_VERSION "@PROJECT_VERSION@")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/EnTTTargets.cmake")
|
||||
check_required_components("@PROJECT_NAME@")
|
||||
8
cmake/in/entt.pc.in
Normal file
8
cmake/in/entt.pc.in
Normal file
@@ -0,0 +1,8 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||
|
||||
Name: EnTT
|
||||
Description: Gaming meets modern C++
|
||||
Url: https://github.com/skypjack/entt
|
||||
Version: @ENTT_VERSION@
|
||||
Cflags: -I${includedir}
|
||||
@@ -1,19 +0,0 @@
|
||||
project(googletest-download NONE)
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
include(ExternalProject)
|
||||
|
||||
ExternalProject_Add(
|
||||
googletest
|
||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||
GIT_TAG master
|
||||
DOWNLOAD_DIR ${GOOGLETEST_DEPS_DIR}
|
||||
TMP_DIR ${GOOGLETEST_DEPS_DIR}/tmp
|
||||
STAMP_DIR ${GOOGLETEST_DEPS_DIR}/stamp
|
||||
SOURCE_DIR ${GOOGLETEST_DEPS_DIR}/src
|
||||
BINARY_DIR ${GOOGLETEST_DEPS_DIR}/build
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
TEST_COMMAND ""
|
||||
)
|
||||
37
conan/build.py
Normal file
37
conan/build.py
Normal file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from cpt.packager import ConanMultiPackager
|
||||
import os
|
||||
|
||||
if __name__ == "__main__":
|
||||
username = os.getenv("GITHUB_ACTOR")
|
||||
tag_version = os.getenv("GITHUB_REF")
|
||||
tag_package = os.getenv("GITHUB_REPOSITORY")
|
||||
login_username = os.getenv("CONAN_LOGIN_USERNAME")
|
||||
package_version = tag_version.replace("refs/tags/v", "")
|
||||
package_name = tag_package.replace("skypjack/", "")
|
||||
reference = "{}/{}".format(package_name, package_version)
|
||||
channel = os.getenv("CONAN_CHANNEL", "stable")
|
||||
upload = os.getenv("CONAN_UPLOAD")
|
||||
stable_branch_pattern = os.getenv("CONAN_STABLE_BRANCH_PATTERN", r"v\d+\.\d+\.\d+.*")
|
||||
test_folder = os.getenv("CPT_TEST_FOLDER", os.path.join("conan", "test_package"))
|
||||
upload_only_when_stable = os.getenv("CONAN_UPLOAD_ONLY_WHEN_STABLE", True)
|
||||
disable_shared = os.getenv("CONAN_DISABLE_SHARED_BUILD", "False")
|
||||
|
||||
builder = ConanMultiPackager(username=username,
|
||||
reference=reference,
|
||||
channel=channel,
|
||||
login_username=login_username,
|
||||
upload=upload,
|
||||
stable_branch_pattern=stable_branch_pattern,
|
||||
upload_only_when_stable=upload_only_when_stable,
|
||||
test_folder=test_folder)
|
||||
builder.add()
|
||||
|
||||
filtered_builds = []
|
||||
for settings, options, env_vars, build_requires, reference in builder.items:
|
||||
if disable_shared == "False" or not options["{}:shared".format(package_name)]:
|
||||
filtered_builds.append([settings, options, env_vars, build_requires])
|
||||
builder.builds = filtered_builds
|
||||
|
||||
builder.run()
|
||||
7
conan/ci/build.sh
Normal file
7
conan/ci/build.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
conan user
|
||||
python conan/build.py
|
||||
6
conan/ci/install.sh
Normal file
6
conan/ci/install.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
pip install -U conan_package_tools conan
|
||||
13
conan/test_package/CMakeLists.txt
Normal file
13
conan/test_package/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
cmake_minimum_required(VERSION 3.7.2)
|
||||
project(test_package)
|
||||
|
||||
set(CMAKE_VERBOSE_MAKEFILE TRUE)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
|
||||
conan_basic_setup()
|
||||
|
||||
add_executable(${PROJECT_NAME} test_package.cpp)
|
||||
target_link_libraries(${PROJECT_NAME} ${CONAN_LIBS})
|
||||
19
conan/test_package/conanfile.py
Normal file
19
conan/test_package/conanfile.py
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from conans import ConanFile, CMake
|
||||
import os
|
||||
|
||||
|
||||
class TestPackageConan(ConanFile):
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
generators = "cmake"
|
||||
|
||||
def build(self):
|
||||
cmake = CMake(self)
|
||||
cmake.configure()
|
||||
cmake.build()
|
||||
|
||||
def test(self):
|
||||
bin_path = os.path.join("bin", "test_package")
|
||||
self.run(bin_path, run_environment=True)
|
||||
56
conan/test_package/test_package.cpp
Normal file
56
conan/test_package/test_package.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <entt/entt.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
struct position {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
struct velocity {
|
||||
float dx;
|
||||
float dy;
|
||||
};
|
||||
|
||||
void update(entt::registry ®istry) {
|
||||
auto view = registry.view<position, velocity>();
|
||||
|
||||
for(auto entity: view) {
|
||||
// gets only the components that are going to be used ...
|
||||
|
||||
auto &vel = view.get<velocity>(entity);
|
||||
|
||||
vel.dx = 0.;
|
||||
vel.dy = 0.;
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
void update(std::uint64_t dt, entt::registry ®istry) {
|
||||
registry.view<position, velocity>().each([dt](auto &pos, auto &vel) {
|
||||
// gets all the components of the view at once ...
|
||||
|
||||
pos.x += vel.dx * dt;
|
||||
pos.y += vel.dy * dt;
|
||||
|
||||
// ...
|
||||
});
|
||||
}
|
||||
|
||||
int main() {
|
||||
entt::registry registry;
|
||||
std::uint64_t dt = 16;
|
||||
|
||||
for(auto i = 0; i < 10; ++i) {
|
||||
auto entity = registry.create();
|
||||
registry.emplace<position>(entity, i * 1.f, i * 1.f);
|
||||
if(i % 2 == 0) { registry.emplace<velocity>(entity, i * .1f, i * .1f); }
|
||||
}
|
||||
|
||||
update(dt, registry);
|
||||
update(registry);
|
||||
|
||||
// ...
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
27
conanfile.py
Normal file
27
conanfile.py
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from conans import ConanFile
|
||||
|
||||
|
||||
class EnttConan(ConanFile):
|
||||
name = "entt"
|
||||
description = "Gaming meets modern C++ - a fast and reliable entity-component system (ECS) and much more "
|
||||
topics = ("conan," "entt", "gaming", "entity", "ecs")
|
||||
url = "https://github.com/skypjack/entt"
|
||||
homepage = url
|
||||
author = "Michele Caini <michele.caini@gmail.com>"
|
||||
license = "MIT"
|
||||
exports = ["LICENSE"]
|
||||
exports_sources = ["src/*"]
|
||||
no_copy_source = True
|
||||
|
||||
def package(self):
|
||||
self.copy(pattern="LICENSE", dst="licenses")
|
||||
self.copy(pattern="*", dst="include", src="src", keep_path=True)
|
||||
|
||||
def package_info(self):
|
||||
if not self.in_local_cache:
|
||||
self.cpp_info.includedirs = ["src"]
|
||||
|
||||
def package_id(self):
|
||||
self.info.header_only()
|
||||
2
deps/.gitignore
vendored
2
deps/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
||||
40
docs/CMakeLists.txt
Normal file
40
docs/CMakeLists.txt
Normal file
@@ -0,0 +1,40 @@
|
||||
#
|
||||
# Doxygen configuration (documentation)
|
||||
#
|
||||
|
||||
set(DOXY_DEPS_DIRECTORY ${EnTT_SOURCE_DIR}/deps)
|
||||
set(DOXY_SOURCE_DIRECTORY ${EnTT_SOURCE_DIR}/src)
|
||||
set(DOXY_DOCS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(DOXY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
configure_file(doxy.in doxy.cfg @ONLY)
|
||||
|
||||
add_custom_target(
|
||||
docs ALL
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.cfg
|
||||
WORKING_DIRECTORY ${EnTT_SOURCE_DIR}
|
||||
VERBATIM
|
||||
SOURCES
|
||||
dox/extra.dox
|
||||
md/config.md
|
||||
md/container.md
|
||||
md/core.md
|
||||
md/entity.md
|
||||
md/faq.md
|
||||
md/lib.md
|
||||
md/links.md
|
||||
md/locator.md
|
||||
md/meta.md
|
||||
md/poly.md
|
||||
md/process.md
|
||||
md/reference.md
|
||||
md/resource.md
|
||||
md/signal.md
|
||||
md/unreal.md
|
||||
doxy.in
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY ${DOXY_OUTPUT_DIRECTORY}/html
|
||||
DESTINATION share/${PROJECT_NAME}-${PROJECT_VERSION}/
|
||||
)
|
||||
5
docs/dox/extra.dox
Normal file
5
docs/dox/extra.dox
Normal file
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* @namespace entt
|
||||
*
|
||||
* @brief `EnTT` default namespace.
|
||||
*/
|
||||
2609
docs/doxy.in
Normal file
2609
docs/doxy.in
Normal file
File diff suppressed because it is too large
Load Diff
111
docs/md/config.md
Normal file
111
docs/md/config.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# Crash Course: configuration
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Definitions](#definitions)
|
||||
* [ENTT_NOEXCEPTION](#entt_noexcept)
|
||||
* [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_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/>
|
||||
In the vast majority of cases, users will have no interest in changing the
|
||||
default parameters. For all other cases, the list of possible configurations
|
||||
with which it's possible to adjust the behavior of the library at runtime can be
|
||||
found below.
|
||||
|
||||
# Definitions
|
||||
|
||||
All options are intended as parameters to the compiler (or user-defined macros
|
||||
within the compilation units, if preferred).<br/>
|
||||
Each parameter can result in internal library definitions. It's not recommended
|
||||
to try to also modify these definitions, since there is no guarantee that they
|
||||
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`.
|
||||
|
||||
## 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
|
||||
thread-safe by means of this definition.
|
||||
|
||||
## ENTT_ID_TYPE
|
||||
|
||||
`entt::id_type` is directly controlled by this definition and widely used within
|
||||
the library.<br/>
|
||||
By default, its type is `std::uint32_t`. However, users can define a different
|
||||
default type if necessary.
|
||||
|
||||
## ENTT_SPARSE_PAGE
|
||||
|
||||
It's known that the ECS module of `EnTT` is based on _sparse sets_. What is less
|
||||
known perhaps is that the sparse arrays are paged to reduce memory usage.<br/>
|
||||
Default size of pages (that is, the number of elements they contain) is 4096 but
|
||||
users can adjust it if appropriate. In all case, the chosen value **must** be a
|
||||
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/>
|
||||
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.
|
||||
|
||||
## ENTT_ASSERT
|
||||
|
||||
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.
|
||||
|
||||
### 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_NO_ETO
|
||||
|
||||
In order to reduce memory consumption and increase performance, empty types are
|
||||
never 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.
|
||||
|
||||
## ENTT_STANDARD_CPP
|
||||
|
||||
`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/>
|
||||
While there are no known portability issues at the time of this writing, this
|
||||
should make the library fully portable anyway if needed.
|
||||
56
docs/md/container.md
Normal file
56
docs/md/container.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Crash Course: containers
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Containers](#containers)
|
||||
* [Dense hash map](#dense-hash-map)
|
||||
* [Dense hash set](#dense-hash-set)
|
||||
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
The standard C++ library offers a wide range of containers and it's really
|
||||
difficult to do better (although it's very easy to do worse, as many examples
|
||||
available online demonstrate).<br/>
|
||||
`EnTT` doesn't try in any way to replace what is offered by the standard. Quite
|
||||
the 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
|
||||
making available some containers initially developed for internal use.
|
||||
|
||||
This section of the library is likely to grow larger over time. However, for the
|
||||
moment it's quite small and mainly aimed at satisfying some internal needs.<br/>
|
||||
For all containers made available, full test coverage and stability over time is
|
||||
guaranteed as usual.
|
||||
|
||||
# Containers
|
||||
|
||||
## Dense hash map
|
||||
|
||||
The dense hash map made available in `EnTT` is a map 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_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.
|
||||
856
docs/md/core.md
Normal file
856
docs/md/core.md
Normal file
@@ -0,0 +1,856 @@
|
||||
# Crash Course: core functionalities
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Unique sequential identifiers](#unique-sequential-identifiers)
|
||||
* [Compile-time generator](#compile-time-generator)
|
||||
* [Runtime generator](#runtime-generator)
|
||||
* [Hashed strings](#hashed-strings)
|
||||
* [Wide characters](wide-characters)
|
||||
* [Conflicts](#conflicts)
|
||||
* [Monostate](#monostate)
|
||||
* [Any as in any type](#any-as-in-any-type)
|
||||
* [Small buffer optimization](#small-buffer-optimization)
|
||||
* [Alignment requirement](#alignment-requirement)
|
||||
* [Type support](#type-support)
|
||||
* [Built-in RTTI support](#built-in-rtti-support)
|
||||
* [Type info](#type-info)
|
||||
* [Almost unique identifiers](#almost-unique-identifiers)
|
||||
* [Type traits](#type-traits)
|
||||
* [Size of](#size-of)
|
||||
* [Is applicable](#is-applicable)
|
||||
* [Constness as](#constness-as)
|
||||
* [Member class type](#member-class-type)
|
||||
* [Integral constant](#integral-constant)
|
||||
* [Tag](#tag)
|
||||
* [Type list and value list](#type-list-and-value-list)
|
||||
* [Compressed pair](#compressed-pair)
|
||||
* [Enum as bitmask](#enum-as-bitmask)
|
||||
* [Utilities](#utilities)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
`EnTT` comes with a bunch of core functionalities mostly used by the other parts
|
||||
of the library itself.<br/>
|
||||
Hardly users will include these features in their code, but it's worth
|
||||
describing what `EnTT` offers so as not to reinvent the wheel in case of need.
|
||||
|
||||
# Unique sequential identifiers
|
||||
|
||||
Sometimes it's useful to be able to give unique, sequential numeric identifiers
|
||||
to types either at compile-time or runtime.<br/>
|
||||
There are plenty of different solutions for this out there and I could have used
|
||||
one of them. However, I decided to spend my time to define a couple of tools
|
||||
that fully embraces what the modern C++ has to offer.
|
||||
|
||||
## Compile-time generator
|
||||
|
||||
To generate sequential numeric identifiers at compile-time, `EnTT` offers the
|
||||
`identifier` class template:
|
||||
|
||||
```cpp
|
||||
// defines the identifiers for the given types
|
||||
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:
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
This is all what this class template has to offer: a `type` inline variable that
|
||||
contains a numeric identifier for the given type. It can be used in any context
|
||||
where constant expressions are required.
|
||||
|
||||
As long as the list remains unchanged, identifiers are also guaranteed to be
|
||||
stable across different runs. In case they have been used in a production
|
||||
environment and a type has to be removed, one can just use a placeholder to left
|
||||
the other identifiers unchanged:
|
||||
|
||||
```cpp
|
||||
template<typename> struct ignore_type {};
|
||||
|
||||
using id = entt::identifier<
|
||||
a_type_still_valid,
|
||||
ignore_type<a_type_no_longer_valid>,
|
||||
another_type_still_valid
|
||||
>;
|
||||
```
|
||||
|
||||
Perhaps a bit ugly to see in a codebase but it gets the job done at least.
|
||||
|
||||
## Runtime generator
|
||||
|
||||
To generate sequential numeric identifiers at runtime, `EnTT` offers the
|
||||
`family` class template:
|
||||
|
||||
```cpp
|
||||
// defines a custom generator
|
||||
using id = entt::family<struct my_tag>;
|
||||
|
||||
// ...
|
||||
|
||||
const auto a_type_id = id::type<a_type>;
|
||||
const auto another_type_id = id::type<another_type>;
|
||||
```
|
||||
|
||||
This is all what a _family_ has to offer: a `type` inline variable that contains
|
||||
a numeric identifier for the given type.<br/>
|
||||
The generator is customizable, so as to get different _sequences_ for different
|
||||
purposes if needed.
|
||||
|
||||
Please, note that identifiers aren't guaranteed to be stable across different
|
||||
runs. Indeed it mostly depends on the flow of execution.
|
||||
|
||||
# Hashed strings
|
||||
|
||||
A hashed string is a zero overhead unique identifier. Users can use
|
||||
human-readable identifiers in the codebase while using their numeric
|
||||
counterparts at runtime, thus without affecting performance.<br/>
|
||||
The class has an implicit `constexpr` constructor that chews a bunch of
|
||||
characters. Once created, all what one can do with it is getting back the
|
||||
original string through the `data` member function or converting the instance
|
||||
into a number.<br/>
|
||||
The good part is that a hashed string can be used wherever a constant expression
|
||||
is required and no _string-to-number_ conversion will take place at runtime if
|
||||
used carefully.
|
||||
|
||||
Example of use:
|
||||
|
||||
```cpp
|
||||
auto load(entt::hashed_string::hash_type resource) {
|
||||
// uses the numeric representation of the resource to load and return it
|
||||
}
|
||||
|
||||
auto resource = load(entt::hashed_string{"gui/background"});
|
||||
```
|
||||
|
||||
There is also a _user defined literal_ dedicated to hashed strings to make them
|
||||
more user-friendly:
|
||||
|
||||
```cpp
|
||||
using namespace entt::literals;
|
||||
constexpr auto str = "text"_hs;
|
||||
```
|
||||
|
||||
To use it, remember that all user defined literals in `EnTT` are enclosed in the
|
||||
`entt::literals` namespace. Therefore, the entire namespace or selectively the
|
||||
literal of interest must be explicitly included before each use, a bit like
|
||||
`std::literals`.<br/>
|
||||
Finally, in case users need to create hashed strings at runtime, this class also
|
||||
offers the necessary functionalities:
|
||||
|
||||
```cpp
|
||||
std::string orig{"text"};
|
||||
|
||||
// create a full-featured hashed string...
|
||||
entt::hashed_string str{orig.c_str()};
|
||||
|
||||
// ... or compute only the unique identifier
|
||||
const auto hash = entt::hashed_string::value(orig.c_str());
|
||||
```
|
||||
|
||||
This possibility shouldn't be exploited in tight loops, since the computation
|
||||
takes place at runtime and no longer at compile-time and could therefore impact
|
||||
performance to some degrees.
|
||||
|
||||
## Wide characters
|
||||
|
||||
The hashed string has a design that is close to that of an `std::basic_string`.
|
||||
It means that `hashed_string` is nothing more than an alias for
|
||||
`basic_hashed_string<char>`. For those who want to use the C++ type for wide
|
||||
character representation, there exists also the alias `hashed_wstring` for
|
||||
`basic_hashed_string<wchar_t>`.<br/>
|
||||
In this case, the user defined literal to use to create hashed strings on the
|
||||
fly is `_hws`:
|
||||
|
||||
```cpp
|
||||
constexpr auto str = L"text"_hws;
|
||||
```
|
||||
|
||||
Note that the hash type of the `hashed_wstring` is the same of its counterpart.
|
||||
|
||||
## Conflicts
|
||||
|
||||
The hashed string class uses internally FNV-1a to compute the numeric
|
||||
counterpart of a string. Because of the _pigeonhole principle_, conflicts are
|
||||
possible. This is a fact.<br/>
|
||||
There is no silver bullet to solve the problem of conflicts when dealing with
|
||||
hashing functions. In this case, the best solution seemed to be to give up.
|
||||
That's all.<br/>
|
||||
After all, human-readable unique identifiers aren't something strictly defined
|
||||
and over which users have not the control. Choosing a slightly different
|
||||
identifier is probably the best solution to make the conflict disappear in this
|
||||
case.
|
||||
|
||||
# Monostate
|
||||
|
||||
The monostate pattern is often presented as an alternative to a singleton based
|
||||
configuration system. This is exactly its purpose in `EnTT`. Moreover, this
|
||||
implementation is thread safe by design (hopefully).<br/>
|
||||
Keys are represented by hashed strings, values are basic types like `int`s or
|
||||
`bool`s. Values of different types can be associated to each key, even more than
|
||||
one at a time. Because of this, users must pay attention to use the same type
|
||||
both during an assignment and when they try to read back their data. Otherwise,
|
||||
they will probably incur in unexpected results.
|
||||
|
||||
Example of use:
|
||||
|
||||
```cpp
|
||||
entt::monostate<entt::hashed_string{"mykey"}>{} = true;
|
||||
entt::monostate<"mykey"_hs>{} = 42;
|
||||
|
||||
// ...
|
||||
|
||||
const bool b = entt::monostate<"mykey"_hs>{};
|
||||
const int i = entt::monostate<entt::hashed_string{"mykey"}>{};
|
||||
```
|
||||
|
||||
# Any as in any type
|
||||
|
||||
`EnTT` comes with its own `any` type. It may seem redundant considering that
|
||||
C++17 introduced `std::any`, but it is not (hopefully).<br/>
|
||||
In fact, the _type_ returned by an `std::any` is a const reference to an
|
||||
`std::type_info`, an implementation defined class that's not something everyone
|
||||
wants to see in a software. Furthermore, there is no way to connect it with the
|
||||
type system of the library and therefore with its integrated RTTI support.<br/>
|
||||
Note that this class is largely used internally by the library itself.
|
||||
|
||||
The API is very similar to that of its most famous counterpart, mainly because
|
||||
this class serves the same purpose of being an opaque container for any type of
|
||||
value.<br/>
|
||||
Instances of `any` also minimize the number of allocations by relying on a well
|
||||
known technique called _small buffer optimization_ and a fake vtable.
|
||||
|
||||
Creating an object of the `any` type, whether empty or not, is trivial:
|
||||
|
||||
```cpp
|
||||
// an empty container
|
||||
entt::any empty{};
|
||||
|
||||
// a container for an int
|
||||
entt::any any{0};
|
||||
|
||||
// in place construction
|
||||
entt::any in_place{std::in_place_type<int>, 42};
|
||||
```
|
||||
|
||||
Alternatively, the `make_any` function serves the same purpose but requires to
|
||||
always be explicit about the type:
|
||||
|
||||
```cpp
|
||||
entt::any any = entt::make_any<int>(42);
|
||||
```
|
||||
|
||||
In both cases, the `any` class takes the burden of destroying the contained
|
||||
element when required, regardless of the storage strategy used for the specific
|
||||
object.<br/>
|
||||
Furthermore, an instance of `any` is not tied to an actual type. Therefore, the
|
||||
wrapper will be reconfigured by assigning it an object of a different type than
|
||||
the one contained, so as to be able to handle the new instance.
|
||||
|
||||
There exists also a way to directly assign a value to the variable contained by
|
||||
an `entt::any`, without necessarily replacing it. This is especially useful when
|
||||
the object is used in _aliasing mode_, as described below:
|
||||
|
||||
```cpp
|
||||
entt::any any{42};
|
||||
entt::any value{3};
|
||||
|
||||
// assigns by copy
|
||||
any.assign(value);
|
||||
|
||||
// assigns by move
|
||||
any.assign(std::move(value));
|
||||
```
|
||||
|
||||
The `any` class will also perform a check on the type information and whether or
|
||||
not the original type was copy or move assignable, as appropriate.<br/>
|
||||
In all cases, the `assign` function returns a boolean value to indicate the
|
||||
success or failure of the operation.
|
||||
|
||||
When in doubt about the type of object contained, the `type` member function of
|
||||
`any` returns a const reference to the `type_info` associated with its element,
|
||||
or `type_id<void>()` if the container is empty. The type is also used internally
|
||||
when comparing two `any` objects:
|
||||
|
||||
```cpp
|
||||
if(any == empty) { /* ... */ }
|
||||
```
|
||||
|
||||
In this case, before proceeding with a comparison, it's verified that the _type_
|
||||
of the two objects is actually the same.<br/>
|
||||
Refer to the `EnTT` type system documentation for more details about how
|
||||
`type_info` works and on possible risks of a comparison.
|
||||
|
||||
A particularly interesting feature of this class is that it can also be used as
|
||||
an opaque container for const and non-const references:
|
||||
|
||||
```cpp
|
||||
int value = 42;
|
||||
|
||||
entt::any any{std::in_place_type<int &>(value)};
|
||||
entt::any cany = entt::make_any<const int &>(value);
|
||||
entt::any fwd = entt::forward_as_any(value);
|
||||
|
||||
any.emplace<const int &>(value);
|
||||
```
|
||||
|
||||
In other words, whenever `any` is explicitly told to construct an _alias_, it
|
||||
acts as a pointer to the original instance rather than making a copy of it or
|
||||
moving it internally. The contained object is never destroyed and users must
|
||||
ensure that its lifetime exceeds that of the container.<br/>
|
||||
Similarly, it's possible to create non-owning copies of `any` from an existing
|
||||
object:
|
||||
|
||||
```cpp
|
||||
// aliasing constructor
|
||||
entt::any ref = other.as_ref();
|
||||
```
|
||||
|
||||
In this case, it doesn't matter if the original container actually holds an
|
||||
object or acts already as a reference for unmanaged elements, the new instance
|
||||
thus created won't create copies and will only serve as a reference for the
|
||||
original item.<br/>
|
||||
This means that, starting from the example above, both `ref` and` other` will
|
||||
point to the same object, whether it's initially contained in `other` or already
|
||||
an unmanaged element.
|
||||
|
||||
As a side note, it's worth mentioning that, while everything works transparently
|
||||
when it comes to non-const references, there are some exceptions when it comes
|
||||
to const references.<br/>
|
||||
In particular, the `data` member function invoked on a non-const instance of
|
||||
`any` that wraps a const reference will return a null pointer in all cases.
|
||||
|
||||
To cast an instance of `any` to a type, the library offers a set of `any_cast`
|
||||
functions in all respects similar to their most famous counterparts.<br/>
|
||||
The only difference is that, in the case of `EnTT`, these won't raise exceptions
|
||||
but will only trigger an assert in debug mode, otherwise resulting in undefined
|
||||
behavior in case of misuse in release mode.
|
||||
|
||||
## Small buffer optimization
|
||||
|
||||
The `any` class uses a technique called _small buffer optimization_ to reduce
|
||||
the number of allocations where possible.<br/>
|
||||
The default reserved size for an instance of `any` is `sizeof(double[2])`.
|
||||
However, this is also configurable if needed. In fact, `any` is defined as an
|
||||
alias for `basic_any<Len>`, where `Len` is the size above.<br/>
|
||||
Users can easily set a custom size or define their own aliases:
|
||||
|
||||
```cpp
|
||||
using my_any = entt::basic_any<sizeof(double[4])>;
|
||||
```
|
||||
|
||||
This feature, in addition to allowing the choice of a size that best suits the
|
||||
needs of an application, also offers the possibility of forcing dynamic creation
|
||||
of objects during construction.<br/>
|
||||
In other terms, if the size is 0, `any` avoids the use of any optimization and
|
||||
always dynamically allocates objects (except for aliasing cases).
|
||||
|
||||
Note that the size of the internal storage as well as the alignment requirements
|
||||
are directly part of the type and therefore contribute to define different types
|
||||
that won't be able to interoperate with each other.
|
||||
|
||||
## Alignment requirement
|
||||
|
||||
The alignment requirement is optional and by default the most stringent (the
|
||||
largest) for any object whose size is at most equal to the one provided.<br/>
|
||||
The `basic_any` class template inspects the alignment requirements in each case,
|
||||
even when not provided and may decide not to use the small buffer optimization
|
||||
in order to meet them.
|
||||
|
||||
The alignment requirement is provided as an optional second parameter following
|
||||
the desired size for the internal storage:
|
||||
|
||||
```cpp
|
||||
using my_any = entt::basic_any<sizeof(double[4]), alignof(double[4])>;
|
||||
```
|
||||
|
||||
Note that the alignment requirements as well as the size of the internal storage
|
||||
are directly part of the type and therefore contribute to define different types
|
||||
that won't be able to interoperate with each other.
|
||||
|
||||
# Type support
|
||||
|
||||
`EnTT` provides some basic information about types of all kinds.<br/>
|
||||
It also offers additional features that are not yet available in the standard
|
||||
library or that will never be.
|
||||
|
||||
## Built-in RTTI support
|
||||
|
||||
Runtime type identification support (or RTTI) is one of the most frequently
|
||||
disabled features in the C++ world, especially in the gaming sector. Regardless
|
||||
of the reasons for this, it's often a shame not to be able to rely on opaque
|
||||
type information at runtime.<br/>
|
||||
The library tries to fill this gap by offering a built-in system that doesn't
|
||||
serve as a replacement but comes very close to being one and offers similar
|
||||
information to that provided by its counterpart.
|
||||
|
||||
Basically, the whole system relies on a handful of classes. In particular:
|
||||
|
||||
* The unique sequential identifier associated with a given type:
|
||||
|
||||
```cpp
|
||||
auto index = entt::type_index<a_type>::value();
|
||||
```
|
||||
|
||||
The returned value isn't guaranteed to be stable across different runs.
|
||||
However, it can be very useful as index in associative and unordered
|
||||
associative containers or for positional accesses in a vector or an array.
|
||||
|
||||
So as not to conflict with the other tools available, the `family` class isn't
|
||||
used to generate these indexes. Therefore, the numeric identifiers returned by
|
||||
the two tools may differ.<br/>
|
||||
On the other hand, this leaves users with full powers over the `family` class
|
||||
and therefore the generation of custom runtime sequences of indices for their
|
||||
own purposes, if necessary.
|
||||
|
||||
An external generator can also be used if needed. In fact, `type_index` can be
|
||||
specialized by type and is also _sfinae-friendly_ in order to allow more
|
||||
refined specializations such as:
|
||||
|
||||
```cpp
|
||||
template<typename Type>
|
||||
struct entt::type_index<Type, std::void_d<decltype(Type::index())>> {
|
||||
static entt::id_type value() ENTT_NOEXCEPT {
|
||||
return Type::index();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Note that indexes **must** still be generated sequentially in this case.<br/>
|
||||
The tool is widely used within `EnTT`. Generating indices not sequentially
|
||||
would break an assumption and would likely lead to undesired behaviors.
|
||||
|
||||
* The hash value associated with a given type:
|
||||
|
||||
```cpp
|
||||
auto hash = entt::type_hash<a_type>::value();
|
||||
```
|
||||
|
||||
In general, the `value` function exposed by `type_hash` is also `constexpr`
|
||||
but this isn't guaranteed for all compilers and platforms (although it's valid
|
||||
with the most well-known and popular ones).
|
||||
|
||||
This function **can** use non-standard features of the language for its own
|
||||
purposes. This makes it possible to provide compile-time identifiers that
|
||||
remain stable across different runs.<br/>
|
||||
In all cases, users can prevent the library from using these features by means
|
||||
of the `ENTT_STANDARD_CPP` definition. In this case, there is no guarantee
|
||||
that identifiers remain stable across executions. Moreover, they are generated
|
||||
at runtime and are no longer a compile-time thing.
|
||||
|
||||
As for `type_index`, also `type_hash` is a _sfinae-friendly_ class that can be
|
||||
specialized in order to customize its behavior globally or on a per-type or
|
||||
per-traits basis.
|
||||
|
||||
* The name associated with a given type:
|
||||
|
||||
```cpp
|
||||
auto name = entt::type_name<a_type>::value();
|
||||
```
|
||||
|
||||
The name associated with a type is extracted from some information generally
|
||||
made available by the compiler in use. Therefore, it may differ depending on
|
||||
the compiler and may be empty in the event that this information isn't
|
||||
available.<br/>
|
||||
For example, given the following class:
|
||||
|
||||
```cpp
|
||||
struct my_type { /* ... */ };
|
||||
```
|
||||
|
||||
The name is `my_type` when compiled with GCC or CLang and `struct my_type`
|
||||
when MSVC is in use.<br/>
|
||||
Most of the time the name is also retrieved at compile-time and is therefore
|
||||
always returned through an `std::string_view`. Users can easily access it and
|
||||
modify it as needed, for example by removing the word `struct` to standardize
|
||||
the result. `EnTT` won't do this for obvious reasons, since it requires
|
||||
copying and creating a new string potentially at runtime.
|
||||
|
||||
This function **can** use non-standard features of the language for its own
|
||||
purposes. Users can prevent the library from using non-standard features by
|
||||
means of the `ENTT_STANDARD_CPP` definition. In this case, the name will be
|
||||
empty by default.
|
||||
|
||||
As for `type_index`, also `type_name` is a _sfinae-friendly_ class that can be
|
||||
specialized in order to customize its behavior globally or on a per-type or
|
||||
per-traits basis.
|
||||
|
||||
These are then combined into utilities that aim to offer an API that is somewhat
|
||||
similar to that offered by the language.
|
||||
|
||||
### Type info
|
||||
|
||||
The `type_info` class isn't a drop-in replacement for `std::type_info` but can
|
||||
provide similar information which are not implementation defined and don't
|
||||
require to enable RTTI.<br/>
|
||||
Therefore, they can sometimes be even more reliable than those obtained
|
||||
otherwise.
|
||||
|
||||
A type info object is an opaque class that is also copy and move constructible.
|
||||
Objects of this class are returned by the `type_id` function template:
|
||||
|
||||
```cpp
|
||||
auto info = entt::type_id<a_type>();
|
||||
```
|
||||
|
||||
These are the information made available by a `type_info` object:
|
||||
|
||||
* The index associated with a given type:
|
||||
|
||||
```cpp
|
||||
auto idx = entt::type_id<a_type>().index();
|
||||
```
|
||||
|
||||
This is also an alias for the following:
|
||||
|
||||
```cpp
|
||||
auto idx = entt::type_index<std::remove_const_t<std::remove_reference_t<a_type>>>::value();
|
||||
```
|
||||
|
||||
* The hash value associated with a given type:
|
||||
|
||||
```cpp
|
||||
auto hash = entt::type_id<a_type>().hash();
|
||||
```
|
||||
|
||||
This is also an alias for the following:
|
||||
|
||||
```cpp
|
||||
auto hash = entt::type_hash<std::remove_const_t<std::remove_reference_t<a_type>>>::value();
|
||||
```
|
||||
|
||||
* The name associated with a given type:
|
||||
|
||||
```cpp
|
||||
auto name = entt::type_id<my_type>().name();
|
||||
```
|
||||
|
||||
This is also an alias for the following:
|
||||
|
||||
```cpp
|
||||
auto name = entt::type_name<std::remove_const_t<std::remove_reference_t<a_type>>>::value();
|
||||
```
|
||||
|
||||
Where all accessed features are available at compile-time, the `type_info` class
|
||||
is also fully `constexpr`. However, this cannot be guaranteed in advance and
|
||||
depends mainly on the compiler in use and any specializations of the classes
|
||||
described above.
|
||||
|
||||
### Almost unique identifiers
|
||||
|
||||
Since the default non-standard, compile-time implementation of `type_hash` makes
|
||||
use of hashed strings, it may happen that two types are assigned the same hash
|
||||
value.<br/>
|
||||
In fact, although this is quite rare, it's not entirely excluded.
|
||||
|
||||
Another case where two types are assigned the same identifier is when classes
|
||||
from different contexts (for example two or more libraries loaded at runtime)
|
||||
have the same fully qualified name. In this case, also `type_name` will return
|
||||
the same value for the two types.<br/>
|
||||
Fortunately, there are several easy ways to deal with this:
|
||||
|
||||
* The most trivial one is to define the `ENTT_STANDARD_CPP` macro. Runtime
|
||||
identifiers don't suffer from the same problem in fact. However, this solution
|
||||
doesn't work well with a plugin system, where the libraries aren't linked.
|
||||
|
||||
* Another possibility is to specialize the `type_name` class for one of the
|
||||
conflicting types, in order to assign it a custom identifier. This is probably
|
||||
the easiest solution that also preserves the feature of the tool.
|
||||
|
||||
* A fully customized identifier generation policy (based for example on enum
|
||||
classes or preprocessing steps) may represent yet another option.
|
||||
|
||||
These are just some examples of possible approaches to the problem but there are
|
||||
many others. As already mentioned above, since users have full control over
|
||||
their types, this problem is in any case easy to solve and should not worry too
|
||||
much.<br/>
|
||||
In all likelihood, it will never happen to run into a conflict anyway.
|
||||
|
||||
## Type traits
|
||||
|
||||
A handful of utilities and traits not present in the standard template library
|
||||
but which can be useful in everyday life.<br/>
|
||||
This list **is not** exhaustive and contains only some of the most useful
|
||||
classes. Refer to the inline documentation for more information on the features
|
||||
offered by this module.
|
||||
|
||||
### Size of
|
||||
|
||||
The standard operator `sizeof` complains when users provide it for example with
|
||||
function or incomplete types. On the other hand, it's guaranteed that its result
|
||||
is always nonzero, even if applied to an empty class type.<br/>
|
||||
This small class combines the two and offers an alternative to `sizeof` that
|
||||
works under all circumstances, returning zero if the type isn't supported:
|
||||
|
||||
```cpp
|
||||
const auto size = entt::size_of_v<void>;
|
||||
```
|
||||
|
||||
### Is applicable
|
||||
|
||||
The standard library offers the great `std::is_invocable` trait in several
|
||||
forms. This takes a function type and a series of arguments and returns true if
|
||||
the condition is satisfied.<br/>
|
||||
Moreover, users are also provided with `std::apply`, a tool for combining
|
||||
invocable elements and tuples of arguments.
|
||||
|
||||
It would therefore be a good idea to have a variant of `std::is_invocable` that
|
||||
also accepts its arguments in the form of a tuple-like type, so as to complete
|
||||
the offer:
|
||||
|
||||
```cpp
|
||||
constexpr bool result = entt::is_applicable<Func, std::tuple<a_type, another_type>>;
|
||||
```
|
||||
|
||||
This trait is built on top of `std::is_invocable` and does nothing but unpack a
|
||||
tuple-like type and simplify the code at the call site.
|
||||
|
||||
### Constness as
|
||||
|
||||
An utility to easily transfer the constness of a type to another type:
|
||||
|
||||
```cpp
|
||||
// type is const dst_type because of the constness of src_type
|
||||
using type = entt::constness_as_t<dst_type, const src_type>;
|
||||
```
|
||||
|
||||
The trait is subject to the rules of the language. Therefore, for example,
|
||||
transferring constness between references won't give the desired effect.
|
||||
|
||||
### Member class type
|
||||
|
||||
The `auto` template parameter introduced with C++17 made it possible to simplify
|
||||
many class templates and template functions but also made the class type opaque
|
||||
when members are passed as template arguments.<br/>
|
||||
The purpose of this utility is to extract the class type in a few lines of code:
|
||||
|
||||
```cpp
|
||||
template<typename Member>
|
||||
using clazz = entt::member_class_t<Member>;
|
||||
```
|
||||
|
||||
### Integral constant
|
||||
|
||||
Since `std::integral_constant` may be annoying because of its form that requires
|
||||
to specify both a type and a value of that type, there is a more user-friendly
|
||||
shortcut for the creation of integral constants.<br/>
|
||||
This shortcut is the alias template `entt::integral_constant`:
|
||||
|
||||
```cpp
|
||||
constexpr auto constant = entt::integral_constant<42>;
|
||||
```
|
||||
|
||||
Among the other uses, when combined with a hashed string it helps to define tags
|
||||
as human-readable _names_ where actual types would be required otherwise:
|
||||
|
||||
```cpp
|
||||
constexpr auto enemy_tag = entt::integral_constant<"enemy"_hs>;
|
||||
registry.emplace<enemy_tag>(entity);
|
||||
```
|
||||
|
||||
### Tag
|
||||
|
||||
Since `id_type` is very important and widely used in `EnTT`, there is a more
|
||||
user-friendly shortcut for the creation of integral constants based on it.<br/>
|
||||
This shortcut is the alias template `entt::tag`.
|
||||
|
||||
If used in combination with hashed strings, it helps to use human-readable names
|
||||
where types would be required otherwise. As an example:
|
||||
|
||||
```cpp
|
||||
registry.emplace<entt::tag<"enemy"_hs>>(entity);
|
||||
```
|
||||
|
||||
However, this isn't the only permitted use. Literally any value convertible to
|
||||
`id_type` is a good candidate, such as the named constants of an unscoped enum.
|
||||
|
||||
### Type list and value list
|
||||
|
||||
There is no respectable library where the much desired _type list_ can be
|
||||
missing.<br/>
|
||||
`EnTT` is no exception and provides (making extensive use of it internally) the
|
||||
`type_list` type, in addition to its `value_list` counterpart dedicated to
|
||||
non-type template parameters.
|
||||
|
||||
Here is a (possibly incomplete) list of the functionalities that come with a
|
||||
type list:
|
||||
|
||||
* `type_list_element[_t]` to get the N-th element of a type list.
|
||||
* `type_list_cat[_t]` and a handy `operator+` to concatenate type lists.
|
||||
* `type_list_unique[_t]` to remove duplicate types from a type list.
|
||||
* `type_list_contains[_v]` to know if a type list contains a given type.
|
||||
* `type_list_diff[_t]` to remove types from type lists.
|
||||
|
||||
I'm also pretty sure that more and more utilities will be added over time as
|
||||
needs become apparent.<br/>
|
||||
Many of these functionalities also exist in their version dedicated to value
|
||||
lists. We therefore have `value_list_element[_v]` as well as
|
||||
`value_list_cat[_t]`and so on.
|
||||
|
||||
# Compressed pair
|
||||
|
||||
Primarily designed for internal use and far from being feature complete, the
|
||||
`compressed_pair` class does exactly what it promises: it tries to reduce the
|
||||
size of a pair by exploiting _Empty Base Class Optimization_ (or _EBCO_).<br/>
|
||||
This class **is not** a drop-in replacement for `std::pair`. However, it offers
|
||||
enough functionalities to be a good alternative for when reducing memory usage
|
||||
is more important than having some cool and probably useless feature.
|
||||
|
||||
Although the API is very close to that of `std::pair` (apart from the fact that
|
||||
the template parameters are inferred from the constructor and therefore there is
|
||||
no` entt::make_compressed_pair`), the major difference is that `first` and
|
||||
`second` are functions for implementation needs:
|
||||
|
||||
```cpp
|
||||
entt::compressed_pair pair{0, 3.};
|
||||
pair.first() = 42;
|
||||
```
|
||||
|
||||
There isn't much to describe then. It's recommended to rely on documentation and
|
||||
intuition. At the end of the day, it's just a pair and nothing more.
|
||||
|
||||
# Enum as bitmask
|
||||
|
||||
Sometimes it's useful to be able to use enums as bitmasks. However, enum classes
|
||||
aren't really suitable for the purpose out of the box. Main problem is that they
|
||||
don't convert implicitly to their underlying type.<br/>
|
||||
All that remains is to make a choice between using old-fashioned enums (with all
|
||||
their problems that I don't want to discuss here) or writing _ugly_ code.
|
||||
|
||||
Fortunately, there is also a third way: adding enough operators in the global
|
||||
scope to treat enum classes as bitmask transparently.<br/>
|
||||
The ultimate goal is to be able to write code like the following (or maybe
|
||||
something more meaningful, but this should give a grasp and remain simple at the
|
||||
same time):
|
||||
|
||||
```cpp
|
||||
enum class my_flag {
|
||||
unknown = 0x01,
|
||||
enabled = 0x02,
|
||||
disabled = 0x04
|
||||
};
|
||||
|
||||
const my_flag flags = my_flag::enabled;
|
||||
const bool is_enabled = !!(flags & my_flag::enabled);
|
||||
```
|
||||
|
||||
The problem with adding all operators to the global scope is that these will
|
||||
come into play even when not required, with the risk of introducing errors that
|
||||
are difficult to deal with.<br/>
|
||||
However, C++ offers enough tools to get around this problem. In particular, the
|
||||
library requires users to register all enum classes for which bitmask support
|
||||
should be enabled:
|
||||
|
||||
```cpp
|
||||
template<>
|
||||
struct entt::enum_as_bitmask<my_flag>
|
||||
: std::true_type
|
||||
{};
|
||||
```
|
||||
|
||||
This is handy when dealing with enum classes defined by third party libraries
|
||||
and over which the users have no control. However, it's also verbose and can be
|
||||
avoided by adding a specific value to the enum class itself:
|
||||
|
||||
```cpp
|
||||
enum class my_flag {
|
||||
unknown = 0x01,
|
||||
enabled = 0x02,
|
||||
disabled = 0x04,
|
||||
_entt_enum_as_bitmask
|
||||
};
|
||||
```
|
||||
|
||||
In this case, there is no need to specialize the `enum_as_bitmask` traits, since
|
||||
`EnTT` will automatically detect the flag and enable the bitmask support.<br/>
|
||||
Once the enum class has been registered (in one way or the other) all the most
|
||||
common operators will be available, such as `&`, `|` but also `&=` and `|=`.
|
||||
Refer to the official documentation for the full list of operators.
|
||||
|
||||
# Utilities
|
||||
|
||||
It's not possible to escape the temptation to add utilities of some kind to a
|
||||
library. In fact, `EnTT` also provides a handful of tools to simplify the
|
||||
life of developers:
|
||||
|
||||
* `entt::identity`: the identity function object that will be available with
|
||||
C++20. It returns its argument unchanged and nothing more. It's useful as a
|
||||
sort of _do nothing_ function in template programming.
|
||||
|
||||
* `entt::overload`: a tool to disambiguate different overloads from their
|
||||
function type. It works with both free and member functions.<br/>
|
||||
Consider the following definition:
|
||||
|
||||
```cpp
|
||||
struct clazz {
|
||||
void bar(int) {}
|
||||
void bar() {}
|
||||
};
|
||||
```
|
||||
|
||||
This utility can be used to get the _right_ overload as:
|
||||
|
||||
```cpp
|
||||
auto *member = entt::overload<void(int)>(&clazz::bar);
|
||||
```
|
||||
|
||||
The line above is literally equivalent to:
|
||||
|
||||
```cpp
|
||||
auto *member = static_cast<void(clazz:: *)(int)>(&clazz::bar);
|
||||
```
|
||||
|
||||
Just easier to read and shorter to type.
|
||||
|
||||
* `entt::overloaded`: a small class template used to create a new type with an
|
||||
overloaded `operator()` from a bunch of lambdas or functors.<br/>
|
||||
As an example:
|
||||
|
||||
```cpp
|
||||
entt::overloaded func{
|
||||
[](int value) { /* ... */ },
|
||||
[](char value) { /* ... */ }
|
||||
};
|
||||
|
||||
func(42);
|
||||
func('c');
|
||||
```
|
||||
|
||||
Rather useful when doing metaprogramming and having to pass to a function a
|
||||
callable object that supports multiple types at once.
|
||||
|
||||
* `entt::y_combinator`: this is a C++ implementation of **the** _y-combinator_.
|
||||
If it's not clear what it is, there is probably no need for this utility.<br/>
|
||||
Below is a small example to show its use:
|
||||
|
||||
```cpp
|
||||
entt::y_combinator gauss([](const auto &self, auto value) -> unsigned int {
|
||||
return value ? (value + self(value-1u)) : 0;
|
||||
});
|
||||
|
||||
const auto result = gauss(3u);
|
||||
```
|
||||
|
||||
Maybe convoluted at a first glance but certainly effective. Unfortunately,
|
||||
the language doesn't make it possible to do much better.
|
||||
|
||||
This is a rundown of the (actually few) utilities made available by `EnTT`. The
|
||||
list will probably grow over time but the size of each will remain rather small,
|
||||
as has been the case so far.
|
||||
2307
docs/md/entity.md
Normal file
2307
docs/md/entity.md
Normal file
File diff suppressed because it is too large
Load Diff
203
docs/md/faq.md
Normal file
203
docs/md/faq.md
Normal file
@@ -0,0 +1,203 @@
|
||||
# Frequently Asked Questions
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [FAQ](#faq)
|
||||
* [Why is my debug build on Windows so slow?](#why-is-my-debug-build-on-windows-so-slow)
|
||||
* [How can I represent hierarchies with my components?](#how-can-i-represent-hierarchies-with-my-components)
|
||||
* [Custom entity identifiers: yay or nay?](#custom-entity-identifiers-yay-or-nay)
|
||||
* [Warning C4307: integral constant overflow](#warning-C4307-integral-constant-overflow)
|
||||
* [Warning C4003: the min, the max and the macro](#warning-C4003-the-min-the-max-and-the-macro)
|
||||
* [The standard and the non-copyable types](#the-standard-and-the-non-copyable-types)
|
||||
* [Which functions trigger which signals](#which-functions-trigger-which-signals)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
This is a constantly updated section where I'll try 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.
|
||||
Probably someone already has an answer for you and we can then integrate this
|
||||
part of the documentation.
|
||||
|
||||
# FAQ
|
||||
|
||||
## Why is my debug build on Windows so slow?
|
||||
|
||||
`EnTT` is an experimental project that I also use to keep me up-to-date with the
|
||||
latest revision of the language and the standard library. For this reason, it's
|
||||
likely that some classes you're working with are using standard containers under
|
||||
the hood.<br/>
|
||||
Unfortunately, it's known that the standard containers aren't particularly
|
||||
performing in debugging (the reasons for this go beyond this document) and are
|
||||
even less so on Windows apparently. Fortunately this can also be mitigated a
|
||||
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
|
||||
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:
|
||||
|
||||
```cpp
|
||||
#define ENTT_ASSERT(...) ((void)0)
|
||||
```
|
||||
|
||||
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`.
|
||||
|
||||
## 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.
|
||||
|
||||
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
|
||||
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
|
||||
far the most convenient solution when it comes to creating hierarchies and
|
||||
whatnot. See the documentation for the ECS part of the library and in particular
|
||||
what concerns the `component_traits` class for further details.
|
||||
|
||||
## Custom entity identifiers: yay or nay?
|
||||
|
||||
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
|
||||
`entity_type` member of type `std::uint32_t` or `std::uint64_t`.<br/>
|
||||
In fact, this is a definition equivalent to that of `entt::entity`:
|
||||
|
||||
```cpp
|
||||
enum class entity: std::uint32_t {};
|
||||
```
|
||||
|
||||
There is no limit to the number of identifiers that can be defined.
|
||||
|
||||
## Warning C4307: integral constant overflow
|
||||
|
||||
According to [this](https://github.com/skypjack/entt/issues/121) issue, using a
|
||||
hashed string under VS could generate a warning.<br/>
|
||||
First of all, I want to reassure you: it's expected and harmless. However, it
|
||||
can be annoying.
|
||||
|
||||
To suppress it and if you don't want to suppress all the other warnings as well,
|
||||
here is a workaround in the form of a macro:
|
||||
|
||||
```cpp
|
||||
#if defined(_MSC_VER)
|
||||
#define HS(str) __pragma(warning(suppress:4307)) entt::hashed_string{str}
|
||||
#else
|
||||
#define HS(str) entt::hashed_string{str}
|
||||
#endif
|
||||
```
|
||||
|
||||
With an example of use included:
|
||||
|
||||
```cpp
|
||||
constexpr auto identifier = HS("my/resource/identifier");
|
||||
```
|
||||
|
||||
Thanks to [huwpascoe](https://github.com/huwpascoe) for the courtesy.
|
||||
|
||||
## Warning C4003: the min, the max and the macro
|
||||
|
||||
On Windows, a header file defines two macros `min` and `max` which may result in
|
||||
conflicts with their counterparts in the standard library and therefore in
|
||||
errors during compilation.
|
||||
|
||||
It's a pretty big problem but fortunately it's not a problem of `EnTT` and there
|
||||
is a fairly simple solution to it.<br/>
|
||||
It consists in defining the `NOMINMAX` macro before to include any other header
|
||||
so as to get rid of the extra definitions:
|
||||
|
||||
```cpp
|
||||
#define NOMINMAX
|
||||
```
|
||||
|
||||
Please refer to [this](https://github.com/skypjack/entt/issues/96) issue for
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
```cpp
|
||||
struct type {
|
||||
std::vector<std::unique_ptr<action>> vec;
|
||||
};
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```cpp
|
||||
struct type {
|
||||
type(const type &) = delete;
|
||||
type & operator=(const type &) = delete;
|
||||
|
||||
std::vector<std::unique_ptr<action>> vec;
|
||||
};
|
||||
```
|
||||
|
||||
Unfortunately, this will also disable aggregate initialization.
|
||||
|
||||
## Which functions trigger which signals
|
||||
|
||||
The `registry` class offers 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.
|
||||
|
||||
Among the most controversial functions can be found `emplace_or_replace` and
|
||||
`destroy`. However, following the above rules, it's quite simple to know what
|
||||
will happen.<br/>
|
||||
In the first case, `on_created` is invoked if the entity has not the component,
|
||||
otherwise the latter is replaced and therefore `on_update` is triggered. As for
|
||||
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.
|
||||
110
docs/md/lib.md
Normal file
110
docs/md/lib.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# Push EnTT across boundaries
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Working across boundaries](#working-across-boundaries)
|
||||
* [Smooth until proven otherwise](#smooth-until-proven-otherwise)
|
||||
* [Meta context](#meta-context)
|
||||
* [Memory management](#memory-management)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Working across boundaries
|
||||
|
||||
`EnTT` has historically had a limit when used across boundaries on Windows in
|
||||
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.
|
||||
|
||||
## 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/>
|
||||
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
|
||||
(definitely uncommon though) or when the default solution proposed by `EnTT`
|
||||
isn't suitable for the user's purposes.<br/>
|
||||
The section dedicated to `type_info` contains all the details to get around the
|
||||
issue in a concise and elegant way. Please refer to the specific documentation.
|
||||
|
||||
When working with linked libraries, compile definitions `ENTT_API_EXPORT` and
|
||||
`ENTT_API_IMPORT` can be used where there is a need 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/>
|
||||
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.
|
||||
|
||||
## Meta context
|
||||
|
||||
The runtime reflection system deserves a special mention when it comes to using
|
||||
it across boundaries.<br/>
|
||||
Since it's linked already to a static context to which the 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.
|
||||
|
||||
Sharing a context is trivial though. First of all, the local one must be
|
||||
acquired in the main space:
|
||||
|
||||
```cpp
|
||||
entt::meta_ctx ctx{};
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```cpp
|
||||
entt::meta_ctx::bind(ctx);
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
## Memory Management
|
||||
|
||||
There is another subtle problem due to memory management that can lead to
|
||||
headaches.<br/>
|
||||
It can occur where there are pools of objects (such as components or events)
|
||||
dynamically created on demand. This is usually not a problem when working with
|
||||
linked libraries that rely on the same dynamic runtime. However, it can occur in
|
||||
the case of plugins or statically linked runtimes.
|
||||
|
||||
As an example, imagine creating an instance of `registry` in the main executable
|
||||
and sharing it with a plugin. If the latter starts working with a component that
|
||||
is unknown to the former, a dedicated pool is created within the registry on
|
||||
first use.<br/>
|
||||
As one can guess, this pool is instantiated on a different side of the boundary
|
||||
from the `registry`. Therefore, the instance is now managing memory from
|
||||
different spaces and this can quickly lead to crashes if not properly addressed.
|
||||
|
||||
To overcome the risk, it's recommended to use well-defined interfaces that make
|
||||
fundamental types pass through the boundaries, isolating the instances of the
|
||||
`EnTT` classes from time to time and as appropriate.<br/>
|
||||
Refer to the test suite for some examples, read the documentation available
|
||||
online about this type of issues or consult someone who has already had such
|
||||
experiences to avoid problems.
|
||||
235
docs/md/links.md
Normal file
235
docs/md/links.md
Normal file
@@ -0,0 +1,235 @@
|
||||
# EnTT in Action
|
||||
|
||||
`EnTT` is widely used in private and commercial applications. I cannot even
|
||||
mention most of them because of some signatures I put on some documents time
|
||||
ago. Fortunately, there are also people who took the time to implement open
|
||||
source projects based on `EnTT` and didn't hold back when it came to documenting
|
||||
them.
|
||||
|
||||
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.
|
||||
|
||||
I hope this list can grow much more in the future:
|
||||
|
||||
* 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 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.
|
||||
* [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
|
||||
reimplementation of the game _Black & White_ (2001).
|
||||
* [Land of the Rair](https://github.com/LandOfTheRair/core2): the new backend
|
||||
of [a retro-style MUD](https://rair.land/) for the new age.
|
||||
* [Face Smash](https://play.google.com/store/apps/details?id=com.gamee.facesmash):
|
||||
a game to play with your face.
|
||||
* [EnTT Pacman](https://github.com/Kerndog73/EnTT-Pacman): an example of how
|
||||
to make Pacman with `EnTT`.
|
||||
* [Wacman](https://github.com/carlfindahl/wacman): a pacman clone with OpenGL.
|
||||
* [Classic Tower Defence](https://github.com/kerndog73/Classic-Tower-Defence):
|
||||
a tiny little tower defence game featuring a homemade font.
|
||||
[Check it out](https://indi-kernick.itch.io/classic-tower-defence).
|
||||
* [The Machine](https://github.com/Kerndog73/The-Machine): a box pushing
|
||||
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.
|
||||
* [Randballs](https://github.com/gale93/randballs): simple `SFML` and `EnTT`
|
||||
playground.
|
||||
* [EnTT Tower Defense](https://github.com/Daivuk/tddod): a data oriented tower
|
||||
defense example.
|
||||
* [EnTT Breakout](https://github.com/vblanco20-1/entt-breakout): simple
|
||||
example of a breakout game, using `SDL` and `EnTT`.
|
||||
* [Arcade puzzle game with EnTT](https://github.com/MasonRG/ArcadePuzzleGame):
|
||||
arcade puzzle game made in C++ using the `SDL2` and `EnTT` libraries.
|
||||
* [Snake with EnTT](https://github.com/MasonRG/SnakeGame): simple snake game
|
||||
made in C++ with the `SDL2` and `EnTT` libraries.
|
||||
* [Mirrors lasers and robots](https://github.com/guillaume-haerinck/imac-tower-defense):
|
||||
a small tower defense game based on mirror orientation.
|
||||
* [PopHead](https://github.com/SPC-Some-Polish-Coders/PopHead/): 2D, Zombie,
|
||||
RPG game made from scratch in C++.
|
||||
* [Robotligan](https://github.com/Trisslotten/robotligan): multiplayer
|
||||
football game.
|
||||
* [DungeonSlayer](https://github.com/alohaeee/DungeonSlayer): 2D game made
|
||||
from scratch in C++.
|
||||
* [3DGame](https://github.com/kwarkGorny/3DGame): 2.5D top-down space shooter.
|
||||
* [Pulcher](https://github.com/AODQ/pulcher): 2D cross-platform game inspired
|
||||
by Quake.
|
||||
* [Destroid](https://github.com/tyrannicaltoucan/destroid): _one-bazillionth_
|
||||
arcade game about shooting dirty rocks in space, inspired by Asteroids.
|
||||
* [Wanderer](https://github.com/albin-johansson/wanderer): a 2D exploration
|
||||
based indie game.
|
||||
* [Spelunky® Classic remake](https://github.com/dbeef/spelunky-psp): a truly
|
||||
multiplatform experience with a rewrite from scratch.
|
||||
* [CubbyTower](https://github.com/utilForever/CubbyTower): a simple tower
|
||||
defense game using C++ with Entity Component System (ECS).
|
||||
* [Runeterra](https://github.com/utilForever/Runeterra): Legends of Runeterra
|
||||
simulator using C++ with some reinforcement learning.
|
||||
* [Black Sun](https://store.steampowered.com/app/1670930/Black_Sun/): fly your
|
||||
space ship through a large 2D open world.
|
||||
* [PokeMaster](https://github.com/utilForever/PokeMaster): Pokemon Battle
|
||||
simulator using C++ with some reinforcement learning.
|
||||
|
||||
* 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
|
||||
agent-based simulations.
|
||||
* [Fling Engine](https://github.com/flingengine/FlingEngine): a Vulkan game
|
||||
engine with a focus on data oriented design.
|
||||
* [NovusCore](https://github.com/novuscore/NovusCore): a modern take on World
|
||||
of Warcraft emulation.
|
||||
* [Chrysalis](https://github.com/ivanhawkes/Chrysalis): action RPG SDK for
|
||||
CRYENGINE games.
|
||||
* [LM-Engine](https://github.com/Lawrencemm/LM-Engine): the Vim of game
|
||||
engines.
|
||||
* [Edyn](https://github.com/xissburg/edyn): a real-time physics engine
|
||||
organized as an ECS.
|
||||
* [MushMachine](https://github.com/MadeOfJelly/MushMachine): engine...
|
||||
vrooooommm.
|
||||
* [Antara Gaming SDK](https://github.com/KomodoPlatform/antara-gaming-sdk):
|
||||
the Komodo Gaming Software Development Kit.
|
||||
* [XVP](https://ravingbots.com/xvp-expansive-vehicle-physics-for-unreal-engine/):
|
||||
[_eXpansive Vehicle Physics_](https://github.com/raving-bots/xvp/wiki/Plugin-integration-guide)
|
||||
plugin for Unreal Engine.
|
||||
* [Apparently](https://teamwisp.github.io/credits/)
|
||||
[Wisp](https://teamwisp.github.io/product/) by
|
||||
[Team Wisp](https://teamwisp.github.io/): an advanced real-time ray tracing
|
||||
renderer built for the demands of video game artists.
|
||||
* [shiva](https://github.com/Milerius/shiva): modern C++ engine with
|
||||
modularity.
|
||||
* [ImGui/EnTT editor](https://github.com/Green-Sky/imgui_entt_entity_editor):
|
||||
a drop-in, single-file entity editor for `EnTT` that uses `ImGui` as
|
||||
graphical backend (with
|
||||
[demo code](https://github.com/Green-Sky/imgui_entt_entity_editor_demo)).
|
||||
* [SgOgl](https://github.com/stwe/SgOgl): a game engine library for OpenGL
|
||||
developed for educational purposes.
|
||||
* [Lumos](https://github.com/jmorton06/Lumos): game engine written in C++
|
||||
using OpenGL and Vulkan.
|
||||
* [Silvanus](https://github.com/hobbyistmaker/silvanus): Silvanus Fusion 360
|
||||
Box Generator.
|
||||
* [Lina Engine](https://github.com/inanevin/LinaEngine): an open-source,
|
||||
modular, tiny and fast C++ game engine, aimed to develop 3D desktop games.
|
||||
* [Spike](https://github.com/FahimFuad/Spike): a powerful game engine which
|
||||
can run on a toaster.
|
||||
* [Helena Framework](https://github.com/NIKEA-SOFT/HelenaFramework): a modern
|
||||
framework in C++17 for backend development.
|
||||
* [Unity/EnTT](https://github.com/TongTungGiang/unity-entt): tech demo of a
|
||||
native simulation layer using `EnTT` and `Unity` as a rendering engine.
|
||||
* [OverEngine](https://github.com/OverShifted/OverEngine): an over-engineered
|
||||
game engine.
|
||||
* [Electro](https://github.com/Electro-Technologies/Electro): high performance
|
||||
3D game engine with a high emphasis on rendering.
|
||||
* [Kawaii](https://github.com/Mathieu-Lala/Kawaii_Engine): a modern data
|
||||
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
|
||||
cross-platform engine created on top of google's filament rendering engine.
|
||||
* [Kaguya](https://github.com/KaiH0717/Kaguya): D3D12 Rendering Engine.
|
||||
* [OpenAWE](https://github.com/OpenAWE-Project/OpenAWE): Open implementation
|
||||
of the Alan Wake Engine.
|
||||
|
||||
* 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.
|
||||
* [Game Engine series](https://www.youtube.com/c/TheChernoProject/videos) by
|
||||
[The Cherno](https://github.com/TheCherno) (not only about `EnTT` but also
|
||||
on the use of an ECS in general):
|
||||
- [Intro to EnTT](https://www.youtube.com/watch?v=D4hz0wEB978).
|
||||
- [Entities and Components](https://www.youtube.com/watch?v=-B1iu4QJTUc).
|
||||
- [The ENTITY Class](https://www.youtube.com/watch?v=GfSzeAcsBb0).
|
||||
- [Camera Systems](https://www.youtube.com/watch?v=ubZn7BlrnTU).
|
||||
- [Scene Camera](https://www.youtube.com/watch?v=UKVFRRufKzo).
|
||||
- [Native Scripting](https://www.youtube.com/watch?v=iIUhg88MK5M).
|
||||
- [Native Scripting (now with virtual functions!)](https://www.youtube.com/watch?v=1cHEcrIn8IQ).
|
||||
- [Scene Hierarchy Panel](https://www.youtube.com/watch?v=wziDnE8guvI).
|
||||
- [Properties Panel](https://www.youtube.com/watch?v=NBpB0qscF3E).
|
||||
- [Camera Component UI](https://www.youtube.com/watch?v=RIMt_6agUiU).
|
||||
- [Drawing Component UI](https://www.youtube.com/watch?v=u3yq8s3KuSE).
|
||||
- [Transform Component UI](https://www.youtube.com/watch?v=8JqcXYbzPJc).
|
||||
- [Adding/Removing Entities and Components UI](https://www.youtube.com/watch?v=PsyGmsIgp9M).
|
||||
- [Saving and Loading Scenes](https://www.youtube.com/watch?v=IEiOP7Y-Mbc).
|
||||
- ... And so on.
|
||||
[Check out](https://www.youtube.com/channel/UCQ-W1KE9EYfdxhL6S4twUNw) the
|
||||
_Game Engine Series_ by The Cherno for more videos.
|
||||
* [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
|
||||
battle built on `UE4`.
|
||||
* [Experimenting with ECS in UE4](http://victor.madtriangles.com/code%20experiment/2018/03/25/post-ue4-ecs-battle.html):
|
||||
interesting article about `UE4` and `EnTT`.
|
||||
* [Implementing ECS architecture in UE4](https://forums.unrealengine.com/development-discussion/c-gameplay-programming/1449913-implementing-ecs-architecture-in-ue4-giant-space-battle):
|
||||
giant space battle.
|
||||
* [Conan Adventures (SFML and EnTT in C++)](https://leinnan.github.io/blog/conan-adventuressfml-and-entt-in-c.html):
|
||||
create projects in modern C++ using `SFML`, `EnTT`, `Conan` and `CMake`.
|
||||
* [Adding EnTT ECS to Chrysalis](https://www.tauradius.com/post/adding-an-ecs-to-chrysalis/):
|
||||
a blog entry (and its
|
||||
[follow-up](https://www.tauradius.com/post/chrysalis-update-2020-08-02/))
|
||||
about the integration of `EnTT` into `Chrysalis`, an action RPG SDK for
|
||||
CRYENGINE games.
|
||||
* [Creating Minecraft in One Week with C++ and Vulkan](https://vazgriz.com/189/creating-minecraft-in-one-week-with-c-and-vulkan/):
|
||||
a crack at recreating Minecraft in one week using a custom C++ engine and
|
||||
Vulkan ([code included](https://github.com/vazgriz/VoxelGame)).
|
||||
* [Ability Creator](https://www.erichildebrand.net/blog/ability-creator-project-retrospect):
|
||||
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.
|
||||
|
||||
* 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
|
||||
enterprise custom apps, as well as by Esri for its own public applications
|
||||
such as
|
||||
[Explorer](https://play.google.com/store/apps/details?id=com.esri.explorer),
|
||||
[Collector](https://play.google.com/store/apps/details?id=com.esri.arcgis.collector)
|
||||
and
|
||||
[Navigator](https://play.google.com/store/apps/details?id=com.esri.navigator).
|
||||
* [FASTSUITE Edition 2](https://www.fastsuite.com/en_EN/fastsuite/fastsuite-edition-2.html)
|
||||
by [Cenit](http://www.cenit.com/en_EN/about-us/overview.html): they use
|
||||
`EnTT` to drive their simulation, that is, the communication between robot
|
||||
controller emulator and renderer.
|
||||
* [Ragdoll](https://ragdolldynamics.com/): real-time physics for Autodesk Maya
|
||||
2020.
|
||||
* [Project Lagrange](https://github.com/adobe/lagrange): a robust geometry
|
||||
processing library by [Adobe](https://github.com/adobe).
|
||||
* [AtomicDEX](https://github.com/KomodoPlatform/atomicDEX-Desktop): a secure
|
||||
wallet and non-custodial decentralized exchange rolled into one application.
|
||||
* [Apparently](https://www.linkedin.com/in/skypjack/)
|
||||
[NIO](https://www.nio.io/): there was a collaboration to make some changes
|
||||
to `EnTT`, at the time used for internal projects.
|
||||
* [Apparently](https://www.linkedin.com/jobs/view/architekt-c%2B%2B-at-tieto-1219512333/)
|
||||
[Tieto](https://www.tieto.com/): they published a job post where `EnTT` was
|
||||
listed on their software stack.
|
||||
* [Sequentity](https://github.com/alanjfs/sequentity): A MIDI-like
|
||||
sequencer/tracker for C++ and `ImGui` (with `Magnum` and `EnTT`).
|
||||
* [EnTT meets Sol2](https://github.com/skaarj1989/entt-meets-sol2): freely
|
||||
available examples of how to combine `EnTT` and `Sol2`.
|
||||
* [Godot meets EnTT](https://github.com/portaloffreedom/godot_entt_example/):
|
||||
a simple example on how to use `EnTT` within
|
||||
[`Godot`](https://godotengine.org/).
|
||||
* [Godot and GameNetworkingSockets meet EnTT](https://github.com/portaloffreedom/godot_entt_net_example):
|
||||
a simple example on how to use `EnTT` and
|
||||
[`GameNetworkingSockets`](https://github.com/ValveSoftware/GameNetworkingSockets)
|
||||
within [`Godot`](https://godotengine.org/).
|
||||
* [MatchOneEntt](https://github.com/mhaemmerle/MatchOneEntt): port of
|
||||
[Match One](https://github.com/sschmid/Match-One) for `Entitas-CSharp`.
|
||||
* 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.
|
||||
75
docs/md/locator.md
Normal file
75
docs/md/locator.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Crash Course: service locator
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Service locator](#service-locator)
|
||||
<!--
|
||||
@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.
|
||||
|
||||
# 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:
|
||||
|
||||
```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();
|
||||
```
|
||||
|
||||
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):
|
||||
|
||||
```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();
|
||||
```
|
||||
|
||||
A common use is to wrap the different locators in a container class, creating
|
||||
aliases for the various services:
|
||||
|
||||
```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...);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
978
docs/md/meta.md
Normal file
978
docs/md/meta.md
Normal file
@@ -0,0 +1,978 @@
|
||||
# Crash Course: runtime reflection system
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Names and identifiers](#names-and-identifiers)
|
||||
* [Reflection in a nutshell](#reflection-in-a-nutshell)
|
||||
* [Any to the rescue](#any-to-the-rescue)
|
||||
* [Enjoy the runtime](#enjoy-the-runtime)
|
||||
* [Container support](#container-support)
|
||||
* [Pointer-like types](#pointer-like-types)
|
||||
* [Template information](#template-information)
|
||||
* [Automatic conversions](#automatic-conversions)
|
||||
* [Implicitly generated default constructor](#implicitly-generated-default-constructor)
|
||||
* [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)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
Reflection (or rather, its lack) is a trending topic in the C++ world and a tool
|
||||
that can unlock a lot of interesting feature 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/>
|
||||
I finally decided to write a built-in, non-intrusive and macro-free runtime
|
||||
reflection system for `EnTT`. Maybe I didn't do better than others or maybe yes,
|
||||
time will tell me, but at least I can model this tool around the library to
|
||||
which it belongs and not the opposite.
|
||||
|
||||
# Names and identifiers
|
||||
|
||||
The meta system doesn't force users to rely on the tools provided by the library
|
||||
when it comes to working with names and identifiers. It does this by offering an
|
||||
API that works with opaque identifiers that may or may not be generated by means
|
||||
of a hashed string.<br/>
|
||||
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
|
||||
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
|
||||
follows:
|
||||
|
||||
```cpp
|
||||
auto factory = entt::meta<my_type>().type("reflected_type"_hs);
|
||||
```
|
||||
|
||||
For what it's worth, this is likely completely equivalent to:
|
||||
|
||||
```cpp
|
||||
auto factory = entt::meta<my_type>().type(42);
|
||||
```
|
||||
|
||||
Obviously, human-readable identifiers are more convenient to use and highly
|
||||
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:
|
||||
|
||||
```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:
|
||||
|
||||
```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:
|
||||
|
||||
```cpp
|
||||
auto factory = entt::meta<my_type>().type();
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```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:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().dtor<&destroy>();
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>()
|
||||
.data<&my_type::static_variable>("static"_hs)
|
||||
.data<&my_type::data_member>("member"_hs)
|
||||
.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:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().data<nullptr, &my_type::data_member>("member"_hs);
|
||||
```
|
||||
|
||||
Multiple setters are also supported by means of a `value_list` object:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().data<entt::value_list<&from_int, &from_string>, &my_type::data_member>("member"_hs);
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>()
|
||||
.func<&my_type::static_function>("static"_hs)
|
||||
.func<&my_type::member_function>("member"_hs)
|
||||
.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/>
|
||||
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:
|
||||
|
||||
```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.
|
||||
|
||||
* _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:
|
||||
|
||||
```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.
|
||||
|
||||
## Any to the rescue
|
||||
|
||||
The reflection system offers a kind of _extended version_ of the `entt::any`
|
||||
class (see the core module for more details).<br/>
|
||||
The purpose is to add some feature on top of those already present, so as to
|
||||
integrate it with the meta type system without having to duplicate the code.
|
||||
|
||||
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
|
||||
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:
|
||||
|
||||
```cpp
|
||||
entt::meta_any empty{};
|
||||
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/>
|
||||
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`.
|
||||
|
||||
## 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:
|
||||
|
||||
```cpp
|
||||
// direct access to a reflected type
|
||||
auto by_type = entt::resolve<my_type>();
|
||||
|
||||
// look up a reflected type by identifier
|
||||
auto by_id = entt::resolve("reflected_type"_hs);
|
||||
|
||||
// look up a reflected type by type info
|
||||
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:
|
||||
|
||||
```cpp
|
||||
for(auto 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.
|
||||
|
||||
The meta objects that compose a meta type are accessed in the following ways:
|
||||
|
||||
* _Meta data_. They are accessed by _name_:
|
||||
|
||||
```cpp
|
||||
auto data = entt::resolve<my_type>().data("member"_hs);
|
||||
```
|
||||
|
||||
The returned type is `meta_data` and may be invalid if there is no meta data
|
||||
object associated with the given identifier.<br/>
|
||||
A meta data object offers an API to query the underlying type (for example, to
|
||||
know if it's a const or a static one), to get the meta type of the variable
|
||||
and to set or get the contained value.
|
||||
|
||||
* _Meta functions_. They are accessed by _name_:
|
||||
|
||||
```cpp
|
||||
auto func = entt::resolve<my_type>().func("member"_hs);
|
||||
```
|
||||
|
||||
The returned type is `meta_func` and may be invalid if there is no meta
|
||||
function object associated with the given identifier.<br/>
|
||||
A meta function object offers an API to query the underlying type (for
|
||||
example, to know if it's a const or a static function), to know the number of
|
||||
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.
|
||||
|
||||
* _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:
|
||||
|
||||
```cpp
|
||||
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:
|
||||
|
||||
```cpp
|
||||
for(auto data: entt::resolve<my_type>().data()) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
A meta type can be 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.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
## Container support
|
||||
|
||||
The runtime reflection system also supports containers of all types.<br/>
|
||||
Moreover, _containers_ doesn't necessarily mean those offered by the C++
|
||||
standard library. In fact, user defined data structures can also work with the
|
||||
meta system in many cases.
|
||||
|
||||
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
|
||||
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
|
||||
_associative containers_.
|
||||
|
||||
It's important to include the header file `container.hpp` to make these
|
||||
specializations available to the compiler when needed.<br/>
|
||||
The same file also contains many examples for the users that are interested in
|
||||
making their own containers available to the meta system.
|
||||
|
||||
When a specialization of the `meta_sequence_container_traits` class exists, the
|
||||
meta system treats the wrapped type as a sequence container. In a similar way,
|
||||
a type is treated as an associative container if a specialization of the
|
||||
`meta_associative_container_traits` class is found for it.<br/>
|
||||
Proxy objects are returned by dedicated members of the `meta_any` class. The
|
||||
following is a deliberately verbose example of how users can access a proxy
|
||||
object for a sequence container:
|
||||
|
||||
```cpp
|
||||
std::vector<int> vec{1, 2, 3};
|
||||
entt::meta_any any = entt::forward_as_meta(vec);
|
||||
|
||||
if(any.type().is_sequence_container()) {
|
||||
if(auto view = any.as_sequence_container(); view) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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/>
|
||||
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.
|
||||
|
||||
The interface of the `meta_sequence_container` proxy object is the same for all
|
||||
types of sequence containers, although the available features differ from case
|
||||
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();
|
||||
```
|
||||
|
||||
* The `resize` member function allows to resize the wrapped container and
|
||||
returns true in case of succes:
|
||||
|
||||
```cpp
|
||||
const bool ok = view.resize(3u);
|
||||
```
|
||||
|
||||
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();
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```cpp
|
||||
for(entt::meta_any element: view) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
In all cases, given an underlying container of type `C`, the returned element
|
||||
contains an object of type `C::value_type` which therefore depends on the
|
||||
actual container.<br/>
|
||||
All meta iterators are input iterators and don't offer an indirection operator
|
||||
on purpose.
|
||||
|
||||
* The `insert` member function can be used to add elements to the container. It
|
||||
accepts a meta iterator and the element to insert:
|
||||
|
||||
```cpp
|
||||
auto last = view.end();
|
||||
// appends an integer to the container
|
||||
view.insert(last, 42);
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
* The `erase` member function can be used to remove elements from the container.
|
||||
It accepts a meta iterator to the element to remove:
|
||||
|
||||
```cpp
|
||||
auto first = view.begin();
|
||||
// removes the first element from the container
|
||||
view.erase(first);
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
* 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:
|
||||
|
||||
```cpp
|
||||
for(std::size_t pos{}, last = view.size(); pos < last; ++pos) {
|
||||
entt::meta_any value = view[pos];
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
Similarly, also the interface of the `meta_associative_container` proxy object
|
||||
is the same for all types of associative containers. However, there are some
|
||||
differences in behavior in the case of key-only containers. In particular:
|
||||
|
||||
* The `key_only` member function returns true if the wrapped container is a
|
||||
key-only one.
|
||||
|
||||
* The `key_type` member function returns the meta type of the keys.
|
||||
|
||||
* The `mapped_type` member function returns an invalid meta type for key-only
|
||||
containers and the meta type of the mapped values for all other types of
|
||||
containers.
|
||||
|
||||
* The `value_type` member function returns the meta type of the elements.<br/>
|
||||
For example, it returns the meta type of `int` for `std::set<int>` while it
|
||||
returns the meta type of `std::pair<const int, char>` for
|
||||
`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();
|
||||
```
|
||||
|
||||
* The `clear` member function allows to clear the wrapped container and returns
|
||||
true in case of success:
|
||||
|
||||
```cpp
|
||||
const bool ok = view.clear();
|
||||
```
|
||||
|
||||
* The `begin` and `end` member functions return opaque iterators that can be
|
||||
used to iterate the container directly:
|
||||
|
||||
```cpp
|
||||
for(std::pair<entt::meta_any, entt::meta_any> element: view) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
In all cases, given an underlying container of type `C`, the returned element
|
||||
is a key-value pair where the key has type `C::key_type` and the value has
|
||||
type `C::mapped_type`. Since key-only containers don't have a mapped type,
|
||||
their _value_ is nothing more than an invalid `meta_any` object.<br/>
|
||||
All meta iterators are input iterators and don't offer an indirection operator
|
||||
on purpose.
|
||||
|
||||
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.
|
||||
|
||||
* 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:
|
||||
|
||||
```cpp
|
||||
auto last = view.end();
|
||||
// appends an integer to the container
|
||||
view.insert(last.handle(), 42, 'c');
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
* 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:
|
||||
|
||||
```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.
|
||||
|
||||
* 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:
|
||||
|
||||
```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.
|
||||
|
||||
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/>
|
||||
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:
|
||||
|
||||
* All types of raw pointers.
|
||||
* `std::unique_ptr` and `std::shared_ptr`.
|
||||
|
||||
It's important to include the header file `pointer.hpp` to make these
|
||||
specializations available to the compiler when needed.<br/>
|
||||
The same file also contains many examples for the users that are interested in
|
||||
making their own pointer-like types available to the meta system.
|
||||
|
||||
When a type is recognized as a pointer-like one by the meta system, it's
|
||||
possible to dereference the instances of `meta_any` that contain these objects.
|
||||
The following is a deliberately verbose example to show how to use this feature:
|
||||
|
||||
```cpp
|
||||
int value = 42;
|
||||
// meta type equivalent to that of int *
|
||||
entt::meta_any any{&value};
|
||||
|
||||
if(any.type().is_pointer_like()) {
|
||||
// meta type equivalent to that of int
|
||||
if(entt::meta_any ref = *any; ref) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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).
|
||||
|
||||
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:
|
||||
|
||||
* It's possible to exploit a solution based on ADL lookup by offering a function
|
||||
(also a template one) named `dereference_meta_pointer_like`:
|
||||
|
||||
```cpp
|
||||
template<typename Type>
|
||||
Type & dereference_meta_pointer_like(const custom_pointer_type<Type> &ptr) {
|
||||
return ptr.deref();
|
||||
}
|
||||
```
|
||||
|
||||
* When not in control of the type's namespace, it's possible to inject into the
|
||||
`entt` namespace a specialization of the `adl_meta_pointer_like` class
|
||||
template to bypass the adl lookup as a whole:
|
||||
|
||||
```cpp
|
||||
template<typename Type>
|
||||
struct entt::adl_meta_pointer_like<custom_pointer_type<Type>> {
|
||||
static decltype(auto) dereference(const custom_pointer_type<Type> &ptr) {
|
||||
return ptr.deref();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
In all other cases, that is, when dereferencing a pointer works as expected and
|
||||
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
|
||||
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
|
||||
available to the compiler when needed.
|
||||
|
||||
Meta template information are easily found:
|
||||
|
||||
```cpp
|
||||
// this method returns true if the type is recognized as a class template specialization
|
||||
if(auto type = entt::resolve<std::shared_ptr<my_type>>(); type.is_template_specialization()) {
|
||||
// meta type of the class template conveniently wrapped by entt::meta_class_template_tag
|
||||
auto class_type = type.template_type();
|
||||
|
||||
// number of template arguments
|
||||
std::size_t arity = type.template_arity();
|
||||
|
||||
// meta type of the i-th argument
|
||||
auto arg_type = type.template_arg(0u);
|
||||
}
|
||||
```
|
||||
|
||||
Typically, when template information for a type are required, what the library
|
||||
provides is sufficient. However, there are some cases where a user may want more
|
||||
details or a different set of information.<br/>
|
||||
Consider the case of a class template that is meant to wrap function types:
|
||||
|
||||
```cpp
|
||||
template<typename>
|
||||
struct function_type;
|
||||
|
||||
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/>
|
||||
To achieve this, users must enter the library internals and provide their own
|
||||
specialization for the class template `entt::meta_template_traits`, such as:
|
||||
|
||||
```cpp
|
||||
template<typename Ret, typename... Args>
|
||||
struct entt::meta_template_traits<function_type<Ret(Args...)>> {
|
||||
using class_type = meta_class_template_tag<function_type>;
|
||||
using args_type = type_list<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.
|
||||
|
||||
## Automatic conversions
|
||||
|
||||
In C++, there are a number of conversions allowed between arithmetic types that
|
||||
make it convenient to work with this kind of data.<br/>
|
||||
If this were to be translated into explicit registrations with the reflection
|
||||
system, it would result in a long series of instructions such as the following:
|
||||
|
||||
```cpp
|
||||
entt::meta<int>()
|
||||
.conv<bool>()
|
||||
.conv<char>()
|
||||
// ...
|
||||
.conv<double>();
|
||||
```
|
||||
|
||||
Repeated for each type eligible to undergo this type of conversions. This is
|
||||
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:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_enum>()
|
||||
.conv<std::underlying_type_t<my_enum>>();
|
||||
```
|
||||
|
||||
Fortunately, all of this can also be avoided. `EnTT` offers implicit support for
|
||||
these types of conversions:
|
||||
|
||||
```cpp
|
||||
entt::meta_any any{42};
|
||||
any.allow_cast<double>();
|
||||
double value = any.cast<double>();
|
||||
```
|
||||
|
||||
With no need for registration, the conversion takes place automatically under
|
||||
the hood. The same goes for a call to `allow_cast` involving a meta type:
|
||||
|
||||
```cpp
|
||||
entt::meta_type type = entt::resolve<int>();
|
||||
entt::meta_any any{my_enum::a_value};
|
||||
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.
|
||||
|
||||
## 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/>
|
||||
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/>
|
||||
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,
|
||||
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.
|
||||
|
||||
## Policies: the more, the less
|
||||
|
||||
Policies are a kind of compile-time directives that can be used when registering
|
||||
reflection information.<br/>
|
||||
Their purpose is to require slightly different behavior than the default in some
|
||||
specific cases. For example, when reading a given data member, its value is
|
||||
returned wrapped in a `meta_any` object which, by default, makes a copy of it.
|
||||
For large objects or if the caller wants to access the original instance, this
|
||||
behavior isn't desirable. Policies are there to offer a solution to this and
|
||||
other problems.
|
||||
|
||||
There are a few alternatives available at the moment:
|
||||
|
||||
* The _as-is_ policy, associated with the type `entt::as_is_t`.<br/>
|
||||
This is the default policy. In general, it should never be used explicitly,
|
||||
since it's implicitly selected if no other policy is specified.<br/>
|
||||
In this case, the return values of the functions as well as the properties
|
||||
exposed as data members are always returned by copy in a dedicated wrapper and
|
||||
therefore associated with their original meta types.
|
||||
|
||||
* The _as-void_ policy, associated with the type `entt::as_void_t`.<br/>
|
||||
Its purpose is to discard the return value of a meta object, whatever it is,
|
||||
thus making it appear as if its type were `void`:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().func<&my_type::member_function, entt::as_void_t>("member"_hs);
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
* 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:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().data<&my_type::data_member, entt::as_ref_t>("member"_hs);
|
||||
```
|
||||
|
||||
These policies work with constructors (for example, when objects are taken
|
||||
from an external container rather than created on demand), data members and
|
||||
functions in general.<br/>
|
||||
If on the one hand `as_cref_t` always forces the return type to be const,
|
||||
`as_ref_t` _adapts_ to the constness of the passed object and to that of the
|
||||
return type if any.
|
||||
|
||||
Some uses are rather trivial, but it's useful to note that there are some less
|
||||
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
|
||||
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 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:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_enum>()
|
||||
.data<my_enum::a_value>("a_value"_hs)
|
||||
.data<my_enum::another_value>("another_value"_hs);
|
||||
|
||||
entt::meta<int>().data<2048>("max_int"_hs);
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```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.
|
||||
|
||||
## 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:
|
||||
|
||||
```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 as key/value pairs:
|
||||
|
||||
```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.
|
||||
|
||||
The meta objects for which properties are supported are currently meta types,
|
||||
meta data and meta functions.<br/>
|
||||
These types also offer a couple of member functions named `prop` to iterate all
|
||||
properties at once or to search a specific property by key:
|
||||
|
||||
```cpp
|
||||
// iterate all properties of a meta type
|
||||
for(auto prop: entt::resolve<my_type>().prop()) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// search for a given property by name
|
||||
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.
|
||||
|
||||
## Unregister types
|
||||
|
||||
A type registered with the reflection system can also be unregistered. This
|
||||
means unregistering all its data members, member functions, conversion functions
|
||||
and so on. However, base classes aren't unregistered as well, since they don't
|
||||
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/>
|
||||
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:
|
||||
|
||||
```cpp
|
||||
entt::meta_reset<my_type>();
|
||||
```
|
||||
|
||||
It's also possible to reset types by their unique identifiers if required:
|
||||
|
||||
```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):
|
||||
|
||||
```cpp
|
||||
entt::meta_reset();
|
||||
```
|
||||
|
||||
All types can be re-registered later with a completely different name and form.
|
||||
359
docs/md/poly.md
Normal file
359
docs/md/poly.md
Normal file
@@ -0,0 +1,359 @@
|
||||
# Crash Course: poly
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Other libraries](#other-libraries)
|
||||
* [Concept and implementation](#concept-and-implementation)
|
||||
* [Deduced interface](#deduced-interface)
|
||||
* [Defined interface](#defined-interface)
|
||||
* [Fullfill a concept](#fullfill-a-concept)
|
||||
* [Inheritance](#inheritance)
|
||||
* [Static polymorphism in the wild](#static-polymorphism-in-the-wild)
|
||||
* [Storage size and alignment requirement](#storage-size-and-alignment-requirement)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
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/>
|
||||
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.
|
||||
|
||||
## Other libraries
|
||||
|
||||
There are some very interesting libraries regarding static polymorphism.<br/>
|
||||
Among all, the two that I prefer are:
|
||||
|
||||
* [`dyno`](https://github.com/ldionne/dyno): runtime polymorphism done right.
|
||||
* [`Poly`](https://github.com/facebook/folly/blob/master/folly/docs/Poly.md):
|
||||
a class template that makes it easy to define a type-erasing polymorphic
|
||||
object wrapper.
|
||||
|
||||
The former is admittedly an experimental library, with many interesting ideas.
|
||||
I've some doubts about the usefulness of some feature in real world projects,
|
||||
but perhaps my lack of experience comes into play here. In my opinion, its only
|
||||
flaw is the API which I find slightly more cumbersome than other solutions.<br/>
|
||||
The latter was undoubtedly a source of inspiration for this module, although I
|
||||
opted for different choices in the implementation of both the final API and some
|
||||
feature.
|
||||
|
||||
Either way, the authors are gurus of the C++ community, people I only have to
|
||||
learn from.
|
||||
|
||||
# Concept and implementation
|
||||
|
||||
The first thing to do to create a _type-erasing polymorphic object wrapper_ (to
|
||||
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
|
||||
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/>
|
||||
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:
|
||||
|
||||
```cpp
|
||||
struct Drawable: entt::type_list<> {
|
||||
template<typename Base>
|
||||
struct type: Base {
|
||||
void draw() { this->template invoke<0>(*this); }
|
||||
};
|
||||
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
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
|
||||
other than `void`:
|
||||
|
||||
```cpp
|
||||
struct Drawable: entt::type_list<> {
|
||||
template<typename Base>
|
||||
struct type: Base {
|
||||
bool draw(int pt) const { return this->template invoke<0>(*this, pt); }
|
||||
};
|
||||
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
In this case, all parameters must be 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
|
||||
external call:
|
||||
|
||||
```cpp
|
||||
struct Drawable: entt::type_list<> {
|
||||
template<typename Base>
|
||||
struct type: Base {
|
||||
void draw() const { entt::poly_call<0>(*this); }
|
||||
};
|
||||
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
Once the _concept_ is defined, users must provide a generic implementation of it
|
||||
in order to tell the system how any type can satisfy its requirements. This is
|
||||
done via an alias template within the concept itself.<br/>
|
||||
The index passed as a template parameter to either `invoke` or `poly_call`
|
||||
refers to how this alias is defined.
|
||||
|
||||
## Defined interface
|
||||
|
||||
A fully defined concept is no different to one for which the interface is
|
||||
deduced, with the only difference that the list of types is not empty this time:
|
||||
|
||||
```cpp
|
||||
struct Drawable: entt::type_list<void()> {
|
||||
template<typename Base>
|
||||
struct type: Base {
|
||||
void draw() { entt::poly_call<0>(*this); }
|
||||
};
|
||||
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
Again, parameters and return values other than `void` are allowed. Also, the
|
||||
function type must be const when the method to bind to it is const:
|
||||
|
||||
```cpp
|
||||
struct Drawable: entt::type_list<bool(int) const> {
|
||||
template<typename Base>
|
||||
struct type: Base {
|
||||
bool draw(int pt) const { return entt::poly_call<0>(*this, pt); }
|
||||
};
|
||||
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
* Either by a class that exposes a member function with the same name and the
|
||||
same signature.
|
||||
|
||||
* Or through a lambda that makes use of existing member functions from the
|
||||
interface itself.
|
||||
|
||||
In other words, it's not possible to make use of functions not belonging to the
|
||||
interface, even if they are present in 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.
|
||||
|
||||
Explicitly defining a static virtual table suppresses the deduction step and
|
||||
allows maximum flexibility when providing the implementation for a concept.
|
||||
|
||||
## Fullfill a concept
|
||||
|
||||
The `impl` alias template of a concept is used to define how it's fulfilled:
|
||||
|
||||
```cpp
|
||||
struct Drawable: entt::type_list<> {
|
||||
// ...
|
||||
|
||||
template<typename Type>
|
||||
using impl = entt::value_list<&Type::draw>;
|
||||
};
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```cpp
|
||||
template<typename Type>
|
||||
void print(Type &self) { self.print(); }
|
||||
|
||||
struct Drawable: entt::type_list<void()> {
|
||||
// ...
|
||||
|
||||
template<typename Type>
|
||||
using impl = entt::value_list<&print<Type>>;
|
||||
};
|
||||
```
|
||||
|
||||
Likewise, as long as the parameter types and return type support conversions to
|
||||
and from those of the function type referenced in the static virtual table, the
|
||||
actual implementation may differ in its function type since it's erased
|
||||
internally.<br/>
|
||||
Moreover, the `self` parameter isn't strictly required by the system and can be
|
||||
left out for free functions if not required.
|
||||
|
||||
Refer to the inline documentation for more details.
|
||||
|
||||
# Inheritance
|
||||
|
||||
_Concept inheritance_ is straightforward due to how poly looks like in `EnTT`.
|
||||
Therefore, it's quite easy to build hierarchies of concepts if necessary.<br/>
|
||||
The only constraint is that all concepts in a hierarchy must belong to the same
|
||||
_family_, that is, they must be either all deduced or all defined.
|
||||
|
||||
For a deduced concept, inheritance is achieved in a few steps:
|
||||
|
||||
```cpp
|
||||
struct DrawableAndErasable: entt::type_list<> {
|
||||
template<typename Base>
|
||||
struct type: typename Drawable::template type<Base> {
|
||||
static constexpr auto base = std::tuple_size_v<typename entt::poly_vtable<Drawable>::type>;
|
||||
void erase() { entt::poly_call<base + 0>(*this); }
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
using impl = entt::value_list_cat_t<
|
||||
typename Drawable::impl<Type>,
|
||||
entt::value_list<&Type::erase>
|
||||
>;
|
||||
};
|
||||
```
|
||||
|
||||
The static virtual table is empty and must remain so.<br/>
|
||||
On the other hand, `type` no longer inherits from `Base` and instead 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/>
|
||||
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/>
|
||||
To do this, it's useful to declare a function that allows to convert a _concept_
|
||||
into its underlying `type_list` object:
|
||||
|
||||
```cpp
|
||||
template<typename... Type>
|
||||
entt::type_list<Type...> as_type_list(const entt::type_list<Type...> &);
|
||||
```
|
||||
|
||||
The definition isn't strictly required, since the function will only be used
|
||||
through a `decltype` as it follows:
|
||||
|
||||
```cpp
|
||||
struct DrawableAndErasable: entt::type_list_cat_t<
|
||||
decltype(as_type_list(std::declval<Drawable>())),
|
||||
entt::type_list<void()>
|
||||
> {
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
Similar to above, `type_list_cat_t` is used to concatenate the underlying static
|
||||
virtual table with the new function types.<br/>
|
||||
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:
|
||||
|
||||
```cpp
|
||||
using drawable = entt::poly<Drawable>;
|
||||
|
||||
struct circle {
|
||||
void draw() { /* ... */ }
|
||||
};
|
||||
|
||||
struct square {
|
||||
void draw() { /* ... */ }
|
||||
};
|
||||
|
||||
// ...
|
||||
|
||||
drawable instance{circle{}};
|
||||
instance->draw();
|
||||
|
||||
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/>
|
||||
Among others, there is also a constructor that allows users to wrap unmanaged
|
||||
objects in a `poly` instance (either const or non-const ones):
|
||||
|
||||
```cpp
|
||||
circle shape;
|
||||
drawable instance{std::in_place_type<circle &>, shape};
|
||||
```
|
||||
|
||||
Similarly, it's possible to create non-owning copies of `poly` from an existing
|
||||
object:
|
||||
|
||||
```cpp
|
||||
drawable other = instance.as_ref();
|
||||
```
|
||||
|
||||
In both cases, although the interface of the `poly` object doesn't change, it
|
||||
won'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.
|
||||
|
||||
# Storage size and alignment requirement
|
||||
|
||||
Under the hood, the `poly` class template makes use of `entt::any`. Therefore,
|
||||
it can take advantage of the possibility of defining at compile-time the size of
|
||||
the storage suitable for the small buffer optimization as well as the alignment
|
||||
requirements:
|
||||
|
||||
```cpp
|
||||
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/>
|
||||
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.
|
||||
212
docs/md/process.md
Normal file
212
docs/md/process.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# Crash Course: cooperative scheduler
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [The process](#the-process)
|
||||
* [Adaptor](#adaptor)
|
||||
* [The scheduler](#the-scheduler)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
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.
|
||||
|
||||
# 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 process should expose publicly the following member functions whether needed
|
||||
(note that it isn't required to define a function unless the derived class wants
|
||||
to _override_ the default behavior):
|
||||
|
||||
* `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.
|
||||
|
||||
* `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.
|
||||
|
||||
* `void succeeded();`
|
||||
|
||||
It's 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
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Here is a minimal example for the sake of curiosity:
|
||||
|
||||
```cpp
|
||||
struct my_process: entt::process<my_process, std::uint32_t> {
|
||||
using delta_type = std::uint32_t;
|
||||
|
||||
my_process(delta_type delay)
|
||||
: remaining{delay}
|
||||
{}
|
||||
|
||||
void update(delta_type delta, void *) {
|
||||
remaining -= std::min(remaining, delta);
|
||||
|
||||
// ...
|
||||
|
||||
if(!remaining) {
|
||||
succeed();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
delta_type remaining;
|
||||
};
|
||||
```
|
||||
|
||||
## Adaptor
|
||||
|
||||
Lambdas and functors can't be used directly with a scheduler for they are not
|
||||
properly defined processes with managed life cycles.<br/>
|
||||
This class helps in filling the gap and turning lambdas and functors into
|
||||
full featured processes usable by a scheduler.
|
||||
|
||||
The 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:
|
||||
|
||||
```cpp
|
||||
void(Delta delta, void *data, auto succeed, auto fail);
|
||||
```
|
||||
|
||||
Parameters have the following meaning:
|
||||
|
||||
* `delta` is the elapsed time.
|
||||
* `data` is an opaque pointer to user data if any, `nullptr` otherwise.
|
||||
* `succeed` is a function to call when a process terminates with success.
|
||||
* `fail` is a function to call when a process terminates with errors.
|
||||
|
||||
Both `succeed` and `fail` accept no parameters at all.
|
||||
|
||||
Note that usually users shouldn't worry about creating adaptors at all. A
|
||||
scheduler creates them internally each and every time a lambda or a functor is
|
||||
used as a process.
|
||||
|
||||
# The scheduler
|
||||
|
||||
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
|
||||
a good candidate to run one more time the next tick.<br/>
|
||||
A process can also have a child. In this case, the parent process is replaced
|
||||
with its child when it terminates and only if it returns with success. In case
|
||||
of errors, both the parent process and its child are discarded. This way, it's
|
||||
easy to create chain of processes to run sequentially.
|
||||
|
||||
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;
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```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();
|
||||
|
||||
// resets the scheduler to its initial state and discards all the processes
|
||||
scheduler.clear();
|
||||
```
|
||||
|
||||
To attach a process to a scheduler there are mainly two ways:
|
||||
|
||||
* If the process inherits from the `process` class template, it's enough to
|
||||
indicate its type and submit all the parameters required to construct it to
|
||||
the `attach` member function:
|
||||
|
||||
```cpp
|
||||
scheduler.attach<my_process>(1000u);
|
||||
```
|
||||
|
||||
* Otherwise, in case of a lambda or a functor, it's enough to provide an
|
||||
instance of the class to the `attach` member function:
|
||||
|
||||
```cpp
|
||||
scheduler.attach([](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/>
|
||||
As a minimal example of use:
|
||||
|
||||
```cpp
|
||||
// schedules a task in the form of a lambda function
|
||||
scheduler.attach([](auto delta, void *, auto succeed, auto fail) {
|
||||
// ...
|
||||
})
|
||||
// appends a child in the form of another lambda function
|
||||
.then([](auto delta, void *, auto succeed, auto fail) {
|
||||
// ...
|
||||
})
|
||||
// appends a child in the form of a process class
|
||||
.then<my_process>(1000u);
|
||||
```
|
||||
|
||||
To update a scheduler and therefore all its processes, the `update` member
|
||||
function is the way to go:
|
||||
|
||||
```cpp
|
||||
// updates all the processes, no user data are provided
|
||||
scheduler.update(delta);
|
||||
|
||||
// updates all the processes and provides them with custom data
|
||||
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:
|
||||
|
||||
```cpp
|
||||
// aborts all the processes abruptly ...
|
||||
scheduler.abort(true);
|
||||
|
||||
// ... or gracefully during the next tick
|
||||
scheduler.abort();
|
||||
```
|
||||
71
docs/md/reference.md
Normal file
71
docs/md/reference.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Similar projects
|
||||
|
||||
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 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.
|
||||
* [Diana](https://github.com/discoloda/Diana): an ECS that uses sparse sets to
|
||||
keep track of entities in systems.
|
||||
* [Flecs](https://github.com/SanderMertens/flecs): a multithreaded archetype
|
||||
ECS based on semi-contiguous arrays rather than chunks.
|
||||
* [lent](https://github.com/nem0/lent): the Donald Trump of the ECS libraries.
|
||||
|
||||
* C++:
|
||||
* [decs](https://github.com/vblanco20-1/decs): a chunk based archetype ECS.
|
||||
* [ecst](https://github.com/SuperV1234/ecst): a multithreaded compile-time
|
||||
ECS that uses sparse sets to keep track of entities in systems.
|
||||
* [EntityX](https://github.com/alecthomas/entityx): a bitset based ECS that
|
||||
uses a single large matrix of components indexed with entities.
|
||||
* [Polypropylene](https://github.com/pmbittner/Polypropylene): a hybrid
|
||||
solution between an ECS and dynamic mixins.
|
||||
|
||||
* C#
|
||||
* [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
|
||||
Component System framework.
|
||||
* [Svelto.ECS](https://github.com/sebas77/Svelto.ECS): a very interesting
|
||||
platform agnostic and table based ECS framework.
|
||||
|
||||
* Go:
|
||||
* [gecs](https://github.com/tutumagi/gecs): a sparse sets based ECS inspired
|
||||
by `EnTT`.
|
||||
|
||||
* Javascript:
|
||||
* [\@javelin/ecs](https://github.com/3mcd/javelin/tree/master/packages/ecs):
|
||||
an archetype ECS in TypeScript.
|
||||
* [ecsy](https://github.com/MozillaReality/ecsy): I haven't had the time to
|
||||
investigate the underlying design of `ecsy` but it looks cool anyway.
|
||||
|
||||
* Perl:
|
||||
* [Game::Entities](https://gitlab.com/jjatria/perl-game-entities): a simple
|
||||
entity registry for ECS designs inspired by `EnTT`.
|
||||
|
||||
* Raku:
|
||||
* [Game::Entities](https://gitlab.com/jjatria/raku-game-entities): a simple
|
||||
entity registry for ECS designs inspired by `EnTT`.
|
||||
|
||||
* Rust:
|
||||
* [Legion](https://github.com/TomGillen/legion): a chunk based archetype ECS.
|
||||
* [Shipyard](https://github.com/leudz/shipyard): it borrows some ideas from
|
||||
`EnTT` and offers a sparse sets based ECS with grouping functionalities.
|
||||
* [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.
|
||||
231
docs/md/resource.md
Normal file
231
docs/md/resource.md
Normal file
@@ -0,0 +1,231 @@
|
||||
# Crash Course: resource management
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [The resource, the loader and the cache](#the-resource-the-loader-and-the-cache)
|
||||
<!--
|
||||
@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
|
||||
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.
|
||||
|
||||
# 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:
|
||||
|
||||
```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:
|
||||
|
||||
```cpp
|
||||
struct my_loader final: entt::resource_loader<my_loader, 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 {
|
||||
// ...
|
||||
return std::shared_ptr<my_resource>(new 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.
|
||||
|
||||
Finally, a cache is a specialization of a class template tailored to a specific
|
||||
resource:
|
||||
|
||||
```cpp
|
||||
using my_cache = entt::resource_cache<my_resource>;
|
||||
|
||||
// ...
|
||||
|
||||
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/>
|
||||
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.
|
||||
|
||||
A cache offers a set of basic functionalities to query its internal state and to
|
||||
_organize_ it:
|
||||
|
||||
```cpp
|
||||
// gets the number of resources managed by a cache
|
||||
const auto size = cache.size();
|
||||
|
||||
// checks if a cache contains at least a valid resource
|
||||
const auto empty = cache.empty();
|
||||
|
||||
// clears a cache and discards its content
|
||||
cache.clear();
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```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"};
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```cpp
|
||||
// uses the identifier declared above
|
||||
cache.load<my_loader>(identifier, 0);
|
||||
|
||||
// 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) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Before trying to load a resource, the `contains` member function can be used to
|
||||
know if a cache already contains a specific resource:
|
||||
|
||||
```cpp
|
||||
auto exists = cache.contains("my/identifier"_hs);
|
||||
```
|
||||
|
||||
There exists also a member function to use to force a reload of an already
|
||||
existing resource if needed:
|
||||
|
||||
```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.
|
||||
581
docs/md/signal.md
Normal file
581
docs/md/signal.md
Normal file
@@ -0,0 +1,581 @@
|
||||
# Crash Course: events, signals and everything in between
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Delegate](#delegate)
|
||||
* [Runtime arguments](#runtime-arguments)
|
||||
* [Lambda support](#lambda-support)
|
||||
* [Signals](#signals)
|
||||
* [Event dispatcher](#event-dispatcher)
|
||||
* [Event emitter](#event-emitter)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# 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.
|
||||
|
||||
The so called _modern C++_ comes with a tool that can be useful in these terms,
|
||||
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
|
||||
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
|
||||
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/>
|
||||
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
|
||||
use it quite a lot anyway.
|
||||
|
||||
The interface is trivial. It offers a default constructor to create empty
|
||||
delegates:
|
||||
|
||||
```cpp
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
```cpp
|
||||
int f(int i) { return i; }
|
||||
|
||||
struct my_struct {
|
||||
int f(const int &i) const { return i; }
|
||||
};
|
||||
|
||||
// bind a free function to the delegate
|
||||
delegate.connect<&f>();
|
||||
|
||||
// bind a member function to the delegate
|
||||
my_struct instance;
|
||||
delegate.connect<&my_struct::f>(instance);
|
||||
```
|
||||
|
||||
The delegate class accepts also 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.
|
||||
|
||||
Free functions having type equivalent to `void(T &, args...)` are accepted as
|
||||
well. The first argument `T &` is considered a payload and the function will
|
||||
receive it back every time it's invoked. In other terms, this works just fine
|
||||
with the above definition:
|
||||
|
||||
```cpp
|
||||
void g(const char &c, int i) { /* ... */ }
|
||||
const char c = 'c';
|
||||
|
||||
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:
|
||||
|
||||
```cpp
|
||||
void g() { /* ... */ }
|
||||
delegate.connect<&g>();
|
||||
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.
|
||||
|
||||
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
|
||||
means of the `entt::connect_arg` variable template:
|
||||
|
||||
```cpp
|
||||
entt::delegate<int(int)> func{entt::connect_arg<&f>};
|
||||
```
|
||||
|
||||
Aside `connect`, a `disconnect` counterpart isn't provided. Instead, there
|
||||
exists a `reset` member function to use to clear a delegate.<br/>
|
||||
To know if a delegate is empty, it can be used explicitly in every conditional
|
||||
statement:
|
||||
|
||||
```cpp
|
||||
if(delegate) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Finally, to invoke a delegate, the function call operator is the way to go as
|
||||
already shown in the examples above:
|
||||
|
||||
```cpp
|
||||
auto ret = delegate(42);
|
||||
```
|
||||
|
||||
In all cases, the 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.
|
||||
|
||||
As a side note, members of classes may or may not be associated with instances.
|
||||
If they are not, the first argument of the function type must be that of the
|
||||
class on which the members operate and an instance of this class must obviously
|
||||
be passed when invoking the delegate:
|
||||
|
||||
```cpp
|
||||
entt::delegate<void(my_struct &, int)> delegate;
|
||||
delegate.connect<&my_struct::f>();
|
||||
|
||||
my_struct instance;
|
||||
delegate(instance, 42);
|
||||
```
|
||||
|
||||
In this case, it's not possible to deduce the function type since the first
|
||||
argument doesn't necessarily have to be a reference (for example, it can be a
|
||||
pointer, as well as a const reference).<br/>
|
||||
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
|
||||
runtime arguments.<br/>
|
||||
When used in this modality, some feature 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
|
||||
supported.
|
||||
* Return type and types of arguments **must** coincide with those of the
|
||||
delegate and _being at least convertible_ isn't enough anymore.
|
||||
|
||||
Moreover, for a given function type `Ret(Args...)`, the signature of the
|
||||
functions connected at runtime must necessarily be `Ret(const void *, Args...)`.
|
||||
|
||||
Runtime arguments can be passed both to the constructor of a delegate and to the
|
||||
`connect` member function. An optional parameter is also accepted in both cases.
|
||||
This argument is used to pass arbitrary user data back and forth as a
|
||||
`const void *` upon invocation.<br/>
|
||||
To connect a function to a delegate _in the hard way_:
|
||||
|
||||
```cpp
|
||||
int func(const void *ptr, int i) { return *static_cast<const int *>(ptr) * i; }
|
||||
const int value = 42;
|
||||
|
||||
// use the constructor ...
|
||||
entt::delegate delegate{&func, &value};
|
||||
|
||||
// ... or the connect member function
|
||||
delegate.connect(&func, &value);
|
||||
```
|
||||
|
||||
The type of the delegate is deduced from the function if possible. In this case,
|
||||
since the first argument is an implementation detail, the deduced function type
|
||||
is `int(int)`.<br/>
|
||||
Invoking a delegate built in this way follows the same rules as previously
|
||||
explained.
|
||||
|
||||
## Lambda support
|
||||
|
||||
In general, the `delegate` class doesn't fully support lambda functions in all
|
||||
their nuances. The reason is pretty simple: a `delegate` isn't a drop-in
|
||||
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.
|
||||
|
||||
This is a logical consequence of the support for connecting functions at
|
||||
runtime. Therefore, lambda functions undergo the same rules and
|
||||
limitations.<br/>
|
||||
In fact, since non-capturing lambda functions decay to pointers to functions,
|
||||
they can be used with a `delegate` as if they were _normal functions_ with
|
||||
optional payload:
|
||||
|
||||
```cpp
|
||||
my_struct instance;
|
||||
|
||||
// use the constructor ...
|
||||
entt::delegate delegate{+[](const void *ptr, int value) {
|
||||
return static_cast<const my_struct *>(ptr)->f(value);
|
||||
}, &instance};
|
||||
|
||||
// ... or the connect member function
|
||||
delegate.connect([](const void *ptr, int value) {
|
||||
return static_cast<const my_struct *>(ptr)->f(value);
|
||||
}, &instance);
|
||||
```
|
||||
|
||||
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)`.
|
||||
|
||||
# Signals
|
||||
|
||||
Signal handlers work with references to classes, function pointers and pointers
|
||||
to members. Listeners can be any kind of objects and users are in charge of
|
||||
connecting and disconnecting them from a signal to avoid crashes due to
|
||||
different lifetimes. On the other side, performance shouldn't be affected that
|
||||
much by the presence of such a signal handler.<br/>
|
||||
Signals make use of delegates internally and therefore they undergo the same
|
||||
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.
|
||||
|
||||
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
|
||||
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
|
||||
function to which they refer:
|
||||
|
||||
```cpp
|
||||
entt::sigh<void(int, char)> signal;
|
||||
```
|
||||
|
||||
Signals offer all the basic functionalities required to know how many listeners
|
||||
they contain (`size`) or if they contain at least a listener (`empty`), as well
|
||||
as a function to use to swap handlers (`swap`).
|
||||
|
||||
Besides them, there are member functions to use both to connect and disconnect
|
||||
listeners in all their forms by means of a sink:
|
||||
|
||||
```cpp
|
||||
void foo(int, char) { /* ... */ }
|
||||
|
||||
struct listener {
|
||||
void bar(const int &, char) { /* ... */ }
|
||||
};
|
||||
|
||||
// ...
|
||||
|
||||
entt::sink sink{signal};
|
||||
listener instance;
|
||||
|
||||
sink.connect<&foo>();
|
||||
sink.connect<&listener::bar>(instance);
|
||||
|
||||
// ...
|
||||
|
||||
// disconnects a free function
|
||||
sink.disconnect<&foo>();
|
||||
|
||||
// disconnect a member function of an instance
|
||||
sink.disconnect<&listener::bar>(instance);
|
||||
|
||||
// disconnect all member functions of an instance, if any
|
||||
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
|
||||
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.
|
||||
|
||||
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`
|
||||
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:
|
||||
|
||||
```cpp
|
||||
int f() { return 0; }
|
||||
int g() { return 1; }
|
||||
|
||||
// ...
|
||||
|
||||
entt::sigh<int()> signal;
|
||||
entt::sink sink{signal};
|
||||
|
||||
sink.connect<&f>();
|
||||
sink.connect<&g>();
|
||||
|
||||
std::vector<int> vec{};
|
||||
signal.collect([&vec](int value) { vec.push_back(value); });
|
||||
|
||||
assert(vec[0] == 0);
|
||||
assert(vec[1] == 1);
|
||||
```
|
||||
|
||||
A collector must expose a function operator that accepts as an argument a type
|
||||
to which the return type of the listeners can be converted. Moreover, it can
|
||||
optionally return a boolean value that is true to stop collecting data, false
|
||||
otherwise. This way one can avoid calling all the listeners in case it isn't
|
||||
necessary.<br/>
|
||||
Functors can also be used in place of a lambda. Since the collector is copied
|
||||
when invoking the `collect` member function, `std::ref` is the way to go in this
|
||||
case:
|
||||
|
||||
```cpp
|
||||
struct my_collector {
|
||||
std::vector<int> vec{};
|
||||
|
||||
bool operator()(int v) noexcept {
|
||||
vec.push_back(v);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// ...
|
||||
|
||||
my_collector collector;
|
||||
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:
|
||||
|
||||
```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:
|
||||
|
||||
```cpp
|
||||
struct an_event { int value; };
|
||||
struct another_event {};
|
||||
|
||||
struct listener {
|
||||
void receive(const an_event &) { /* ... */ }
|
||||
void method(const another_event &) { /* ... */ }
|
||||
};
|
||||
|
||||
// ...
|
||||
|
||||
listener listener;
|
||||
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:
|
||||
|
||||
```cpp
|
||||
dispatcher.sink<an_event>().disconnect<&listener::receive>(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:
|
||||
|
||||
```cpp
|
||||
dispatcher.trigger<an_event>(42);
|
||||
dispatcher.trigger<another_event>();
|
||||
```
|
||||
|
||||
Listeners are invoked immediately, order of execution isn't guaranteed. This
|
||||
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`:
|
||||
|
||||
```cpp
|
||||
dispatcher.enqueue<an_event>(42);
|
||||
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:
|
||||
|
||||
```cpp
|
||||
// emits all the events of the given type at once
|
||||
dispatcher.update<an_event>();
|
||||
|
||||
// emits all the events queued so far at once
|
||||
dispatcher.update();
|
||||
```
|
||||
|
||||
This way users can embed the dispatcher in a loop and literally dispatch events
|
||||
once per tick to their systems.
|
||||
|
||||
# Event emitter
|
||||
|
||||
A general purpose event emitter thought mainly for those cases where it comes to
|
||||
working with asynchronous stuff.<br/>
|
||||
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:
|
||||
|
||||
```cpp
|
||||
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.
|
||||
|
||||
To create new instances of an emitter, no arguments are required:
|
||||
|
||||
```cpp
|
||||
my_emitter emitter{};
|
||||
```
|
||||
|
||||
Listeners must be movable and callable objects (free functions, lambdas,
|
||||
functors, `std::function`s, whatever) whose function type is compatible with:
|
||||
|
||||
```cpp
|
||||
void(Event &, 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:
|
||||
|
||||
```cpp
|
||||
emitter.erase(conn);
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```cpp
|
||||
// removes all the listener for the specific event
|
||||
emitter.clear<my_event>();
|
||||
|
||||
// removes all the listeners registered so far
|
||||
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:
|
||||
|
||||
```cpp
|
||||
struct my_event { int i; };
|
||||
|
||||
// ...
|
||||
|
||||
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:
|
||||
|
||||
```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();
|
||||
```
|
||||
|
||||
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.
|
||||
107
docs/md/unreal.md
Normal file
107
docs/md/unreal.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# EnTT and Unreal Engine
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Enable Cpp17](#enable-cpp17)
|
||||
* [EnTT as a third party module](#entt-as-a-third-party-module)
|
||||
* [Include EnTT](#include-entt)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
## Enable Cpp17
|
||||
|
||||
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
|
||||
the main module of the project there should be a `<Game Name>.Build.cs` file,
|
||||
the constructor of which must contain the following lines:
|
||||
|
||||
```cs
|
||||
PCHUsage = PCHUsageMode.NoSharedPCHs;
|
||||
PrivatePCHHeaderFile = "<PCH filename>.h";
|
||||
CppStandard = CppStandardVersion.Cpp17;
|
||||
```
|
||||
|
||||
Replace `<PCH filename>.h` with the name of the already existing PCH header
|
||||
file, if any.<br/>
|
||||
In case the project doesn't already contain a file of this type, it's possible
|
||||
to create one with the following content:
|
||||
|
||||
```cpp
|
||||
#pragma once
|
||||
#include "CoreMinimal.h"
|
||||
```
|
||||
|
||||
Remember to remove any old `PCHUsage = <...>` line that was previously there. At
|
||||
this point, C++17 support should be in place.<br/>
|
||||
Try to compile the project to ensure it works as expected before following
|
||||
further steps.
|
||||
|
||||
Note that updating a *project* to C++17 doesn't necessarily mean that the IDE in
|
||||
use will also start to recognize its syntax.<br/>
|
||||
If the plan is to use C++17 in the project too, check the specific instructions
|
||||
for the IDE in use.
|
||||
|
||||
## EnTT as a third party module
|
||||
|
||||
Once this point is reached, the `Source` directory should look like this:
|
||||
|
||||
```
|
||||
Source
|
||||
| MyGame.Target.cs
|
||||
| MyGameEditor.Target.cs
|
||||
|
|
||||
+---MyGame
|
||||
| | MyGame.Build.cs
|
||||
| | MyGame.h (PCH Header file)
|
||||
|
|
||||
\---ThirdParty
|
||||
\---EnTT
|
||||
| EnTT.Build.cs
|
||||
|
|
||||
\---entt (GitHub repository content inside)
|
||||
```
|
||||
|
||||
To make this happen, create the folder `ThirdParty` under `Source` if it doesn't
|
||||
exist already. Then, add an `EnTT` folder under `ThirdParty`.<br/>
|
||||
Within the latter, create a new file `EnTT.Build.cs` with the following content:
|
||||
|
||||
```cs
|
||||
using System.IO;
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class EnTT: ModuleRules {
|
||||
public EnTT(ReadOnlyTargetRules Target) : base(Target) {
|
||||
Type = ModuleType.External;
|
||||
PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "entt", "src", "entt"));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The last line indicates that the actual files will be found in the directory
|
||||
`EnTT/entt/src/entt`.<br/>
|
||||
Download the repository for `EnTT` and place it next to `EnTT.Build.cs` or
|
||||
update the path above accordingly.
|
||||
|
||||
Finally, open the `<Game Name>.Build.cs` file and add `EnTT` as a dependency at
|
||||
the end of the list:
|
||||
|
||||
```cs
|
||||
PublicDependencyModuleNames.AddRange(new[] {
|
||||
"Core", "CoreUObject", "Engine", "InputCore", [...], "EnTT"
|
||||
});
|
||||
```
|
||||
|
||||
Note that some IDEs might require a restart to start recognizing the new module
|
||||
for code-highlighting features and such.
|
||||
|
||||
## Include EnTT
|
||||
|
||||
In any source file of the project, add `#include "entt.hpp"` or any other path
|
||||
to the file from `EnTT` to use it.<br/>
|
||||
Try to create a registry as `entt::registry registry;` to make sure everything
|
||||
compiles fine.
|
||||
3
natvis/entt/config.natvis
Normal file
3
natvis/entt/config.natvis
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
</AutoVisualizer>
|
||||
33
natvis/entt/container.natvis
Normal file
33
natvis/entt/container.natvis
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::dense_hash_map<*>">
|
||||
<Intrinsic Name="size" Expression="packed.first_base::value.size()"/>
|
||||
<Intrinsic Name="bucket_count" Expression="sparse.first_base::value.size()"/>
|
||||
<DisplayString>{{ size={ size() } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[capacity]" ExcludeView="simple">packed.first_base::value.capacity()</Item>
|
||||
<Item Name="[bucket_count]" ExcludeView="simple">bucket_count()</Item>
|
||||
<Item Name="[load_factor]" ExcludeView="simple">(float)size() / (float)bucket_count()</Item>
|
||||
<Item Name="[max_load_factor]" ExcludeView="simple">threshold</Item>
|
||||
<IndexListItems>
|
||||
<Size>size()</Size>
|
||||
<ValueNode>packed.first_base::value[$i].element</ValueNode>
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::dense_hash_set<*>">
|
||||
<Intrinsic Name="size" Expression="packed.first_base::value.size()"/>
|
||||
<Intrinsic Name="bucket_count" Expression="sparse.first_base::value.size()"/>
|
||||
<DisplayString>{{ size={ size() } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[capacity]" ExcludeView="simple">packed.first_base::value.capacity()</Item>
|
||||
<Item Name="[bucket_count]" ExcludeView="simple">bucket_count()</Item>
|
||||
<Item Name="[load_factor]" ExcludeView="simple">(float)size() / (float)bucket_count()</Item>
|
||||
<Item Name="[max_load_factor]" ExcludeView="simple">threshold</Item>
|
||||
<IndexListItems>
|
||||
<Size>size()</Size>
|
||||
<ValueNode>packed.first_base::value[$i].element</ValueNode>
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
31
natvis/entt/core.natvis
Normal file
31
natvis/entt/core.natvis
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::basic_any<*>">
|
||||
<DisplayString>{{ type={ info->alias,na }, policy={ mode,en } }}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::compressed_pair<*>">
|
||||
<Intrinsic Name="first" Optional="true" Expression="((first_base*)this)->value"/>
|
||||
<Intrinsic Name="first" Optional="true" Expression="*(first_base::base_type*)this"/>
|
||||
<Intrinsic Name="second" Optional="true" Expression="((second_base*)this)->value"/>
|
||||
<Intrinsic Name="second" Optional="true" Expression="*(second_base::base_type*)this"/>
|
||||
<DisplayString >({ first() }, { second() })</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[first]">first()</Item>
|
||||
<Item Name="[second]">second()</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_hashed_string<*>">
|
||||
<DisplayString Condition="str != nullptr">{{ hash={ hash } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[data]">str,na</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::type_info">
|
||||
<DisplayString>{{ name={ alias,na } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[hash]">identifier</Item>
|
||||
<Item Name="[index]">seq</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
119
natvis/entt/entity.natvis
Normal file
119
natvis/entt/entity.natvis
Normal file
@@ -0,0 +1,119 @@
|
||||
<?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>
|
||||
<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>
|
||||
<Synthetic Name="[pools]">
|
||||
<DisplayString>{ pools_size() }</DisplayString>
|
||||
<Expand>
|
||||
<IndexListItems ExcludeView="simple">
|
||||
<Size>pools_size()</Size>
|
||||
<ValueNode>*pools.packed.first_base::value[$i].element.second</ValueNode>
|
||||
</IndexListItems>
|
||||
<IndexListItems IncludeView="simple">
|
||||
<Size>pools_size()</Size>
|
||||
<ValueNode>*pools.packed.first_base::value[$i].element.second,view(simple)</ValueNode>
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Item Name="[groups]" ExcludeView="simple">groups.size()</Item>
|
||||
<Synthetic Name="[vars]">
|
||||
<DisplayString>{ vars_size() }</DisplayString>
|
||||
<Expand>
|
||||
<IndexListItems>
|
||||
<Size>vars_size()</Size>
|
||||
<ValueNode>vars.packed.first_base::value[$i].element.second</ValueNode>
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_sparse_set<*>">
|
||||
<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>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_storage<*>">
|
||||
<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="[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">
|
||||
<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>
|
||||
<Exec>++pos</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_view<*>">
|
||||
<DisplayString>{{ size_hint={ view->packed.size() } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[pools]">pools,na</Item>
|
||||
<Item Name="[filter]">filter,na</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::null_t">
|
||||
<DisplayString><null></DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::tombstone_t">
|
||||
<DisplayString><tombstone></DisplayString>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
3
natvis/entt/locator.natvis
Normal file
3
natvis/entt/locator.natvis
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
</AutoVisualizer>
|
||||
197
natvis/entt/meta.natvis
Normal file
197
natvis/entt/meta.natvis
Normal file
@@ -0,0 +1,197 @@
|
||||
<?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>
|
||||
</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>
|
||||
<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>
|
||||
</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>
|
||||
<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>
|
||||
</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>
|
||||
</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>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_type_node">
|
||||
<Intrinsic Name="has_property" Expression="!!(traits & property)">
|
||||
<Parameter Name="property" Type="int"/>
|
||||
</Intrinsic>
|
||||
<DisplayString Condition="info != nullptr">{{ type={ info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[id]">id</Item>
|
||||
<Item Name="[sizeof]">size_of</Item>
|
||||
<Item Name="[is_arithmetic]">has_property(entt::internal::meta_traits::is_arithmetic)</Item>
|
||||
<Item Name="[is_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>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_type">
|
||||
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
3
natvis/entt/platform.natvis
Normal file
3
natvis/entt/platform.natvis
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
</AutoVisualizer>
|
||||
6
natvis/entt/poly.natvis
Normal file
6
natvis/entt/poly.natvis
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::basic_poly<*>">
|
||||
<DisplayString>{ storage }</DisplayString>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
3
natvis/entt/process.natvis
Normal file
3
natvis/entt/process.natvis
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
</AutoVisualizer>
|
||||
3
natvis/entt/resource.natvis
Normal file
3
natvis/entt/resource.natvis
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
</AutoVisualizer>
|
||||
52
natvis/entt/signal.natvis
Normal file
52
natvis/entt/signal.natvis
Normal file
@@ -0,0 +1,52 @@
|
||||
<?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>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::dispatcher">
|
||||
<DisplayString>{{ size={ pools.size() } }}</DisplayString>
|
||||
<Expand>
|
||||
<Synthetic Name="[pools]">
|
||||
<DisplayString>{ pools.size() }</DisplayString>
|
||||
<Expand>
|
||||
<IndexListItems>
|
||||
<Size>pools.size()</Size>
|
||||
<ValueNode>*pools.packed.first_base::value[$i].element.second</ValueNode>
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::dispatcher::pool_handler<*>">
|
||||
<DisplayString>{{ size={ events.size() }, event={ "$T1" } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[signal]">signal</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::scoped_connection">
|
||||
<DisplayString>{ conn }</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::sigh<*>">
|
||||
<DisplayString>{{ size={ calls.size() }, type={ "$T1" } }}</DisplayString>
|
||||
<Expand>
|
||||
<IndexListItems>
|
||||
<Size>calls.size()</Size>
|
||||
<ValueNode>calls[$i]</ValueNode>
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::sink<*>">
|
||||
<DisplayString>{{ type={ "$T1" } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[signal]">signal,na</Item>
|
||||
<Item Name="[offset]">offset</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
299
scripts/amalgamate.py
Normal file
299
scripts/amalgamate.py
Normal file
@@ -0,0 +1,299 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
# amalgamate.py - Amalgamate C source and header files.
|
||||
# Copyright (c) 2012, Erik Edlund <erik.edlund@32767.se>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# * Neither the name of Erik Edlund, nor the names of its contributors may
|
||||
# be used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
class Amalgamation(object):
|
||||
|
||||
# Prepends self.source_path to file_path if needed.
|
||||
def actual_path(self, file_path):
|
||||
if not os.path.isabs(file_path):
|
||||
file_path = os.path.join(self.source_path, file_path)
|
||||
return file_path
|
||||
|
||||
# Search included file_path in self.include_paths and
|
||||
# in source_dir if specified.
|
||||
def find_included_file(self, file_path, source_dir):
|
||||
search_dirs = self.include_paths[:]
|
||||
if source_dir:
|
||||
search_dirs.insert(0, source_dir)
|
||||
|
||||
for search_dir in search_dirs:
|
||||
search_path = os.path.join(search_dir, file_path)
|
||||
if os.path.isfile(self.actual_path(search_path)):
|
||||
return search_path
|
||||
return None
|
||||
|
||||
def __init__(self, args):
|
||||
with open(args.config, 'r') as f:
|
||||
config = json.loads(f.read())
|
||||
for key in config:
|
||||
setattr(self, key, config[key])
|
||||
|
||||
self.verbose = args.verbose == "yes"
|
||||
self.prologue = args.prologue
|
||||
self.source_path = args.source_path
|
||||
self.included_files = []
|
||||
|
||||
# Generate the amalgamation and write it to the target file.
|
||||
def generate(self):
|
||||
amalgamation = ""
|
||||
|
||||
if self.prologue:
|
||||
with open(self.prologue, 'r') as f:
|
||||
amalgamation += datetime.datetime.now().strftime(f.read())
|
||||
|
||||
if self.verbose:
|
||||
print("Config:")
|
||||
print(" target = {0}".format(self.target))
|
||||
print(" working_dir = {0}".format(os.getcwd()))
|
||||
print(" include_paths = {0}".format(self.include_paths))
|
||||
print("Creating amalgamation:")
|
||||
for file_path in self.sources:
|
||||
# Do not check the include paths while processing the source
|
||||
# list, all given source paths must be correct.
|
||||
# actual_path = self.actual_path(file_path)
|
||||
print(" - processing \"{0}\"".format(file_path))
|
||||
t = TranslationUnit(file_path, self, True)
|
||||
amalgamation += t.content
|
||||
|
||||
with open(self.target, 'w') as f:
|
||||
f.write(amalgamation)
|
||||
|
||||
print("...done!\n")
|
||||
if self.verbose:
|
||||
print("Files processed: {0}".format(self.sources))
|
||||
print("Files included: {0}".format(self.included_files))
|
||||
print("")
|
||||
|
||||
|
||||
def _is_within(match, matches):
|
||||
for m in matches:
|
||||
if match.start() > m.start() and \
|
||||
match.end() < m.end():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class TranslationUnit(object):
|
||||
# // C++ comment.
|
||||
cpp_comment_pattern = re.compile(r"//.*?\n")
|
||||
|
||||
# /* C comment. */
|
||||
c_comment_pattern = re.compile(r"/\*.*?\*/", re.S)
|
||||
|
||||
# "complex \"stri\\\ng\" value".
|
||||
string_pattern = re.compile("[^']" r'".*?(?<=[^\\])"', re.S)
|
||||
|
||||
# Handle simple include directives. Support for advanced
|
||||
# directives where macros and defines needs to expanded is
|
||||
# not a concern right now.
|
||||
include_pattern = re.compile(
|
||||
r'#\s*include\s+(<|")(?P<path>.*?)("|>)', re.S)
|
||||
|
||||
# #pragma once
|
||||
pragma_once_pattern = re.compile(r'#\s*pragma\s+once', re.S)
|
||||
|
||||
# Search for pattern in self.content, add the match to
|
||||
# contexts if found and update the index accordingly.
|
||||
def _search_content(self, index, pattern, contexts):
|
||||
match = pattern.search(self.content, index)
|
||||
if match:
|
||||
contexts.append(match)
|
||||
return match.end()
|
||||
return index + 2
|
||||
|
||||
# Return all the skippable contexts, i.e., comments and strings
|
||||
def _find_skippable_contexts(self):
|
||||
# Find contexts in the content in which a found include
|
||||
# directive should not be processed.
|
||||
skippable_contexts = []
|
||||
|
||||
# Walk through the content char by char, and try to grab
|
||||
# skippable contexts using regular expressions when found.
|
||||
i = 1
|
||||
content_len = len(self.content)
|
||||
while i < content_len:
|
||||
j = i - 1
|
||||
current = self.content[i]
|
||||
previous = self.content[j]
|
||||
|
||||
if current == '"':
|
||||
# String value.
|
||||
i = self._search_content(j, self.string_pattern,
|
||||
skippable_contexts)
|
||||
elif current == '*' and previous == '/':
|
||||
# C style comment.
|
||||
i = self._search_content(j, self.c_comment_pattern,
|
||||
skippable_contexts)
|
||||
elif current == '/' and previous == '/':
|
||||
# C++ style comment.
|
||||
i = self._search_content(j, self.cpp_comment_pattern,
|
||||
skippable_contexts)
|
||||
else:
|
||||
# Skip to the next char.
|
||||
i += 1
|
||||
|
||||
return skippable_contexts
|
||||
|
||||
# Returns True if the match is within list of other matches
|
||||
|
||||
# Removes pragma once from content
|
||||
def _process_pragma_once(self):
|
||||
content_len = len(self.content)
|
||||
if content_len < len("#include <x>"):
|
||||
return 0
|
||||
|
||||
# Find contexts in the content in which a found include
|
||||
# directive should not be processed.
|
||||
skippable_contexts = self._find_skippable_contexts()
|
||||
|
||||
pragmas = []
|
||||
pragma_once_match = self.pragma_once_pattern.search(self.content)
|
||||
while pragma_once_match:
|
||||
if not _is_within(pragma_once_match, skippable_contexts):
|
||||
pragmas.append(pragma_once_match)
|
||||
|
||||
pragma_once_match = self.pragma_once_pattern.search(self.content,
|
||||
pragma_once_match.end())
|
||||
|
||||
# Handle all collected pragma once directives.
|
||||
prev_end = 0
|
||||
tmp_content = ''
|
||||
for pragma_match in pragmas:
|
||||
tmp_content += self.content[prev_end:pragma_match.start()]
|
||||
prev_end = pragma_match.end()
|
||||
tmp_content += self.content[prev_end:]
|
||||
self.content = tmp_content
|
||||
|
||||
# Include all trivial #include directives into self.content.
|
||||
def _process_includes(self):
|
||||
content_len = len(self.content)
|
||||
if content_len < len("#include <x>"):
|
||||
return 0
|
||||
|
||||
# Find contexts in the content in which a found include
|
||||
# directive should not be processed.
|
||||
skippable_contexts = self._find_skippable_contexts()
|
||||
|
||||
# Search for include directives in the content, collect those
|
||||
# which should be included into the content.
|
||||
includes = []
|
||||
include_match = self.include_pattern.search(self.content)
|
||||
while include_match:
|
||||
if not _is_within(include_match, skippable_contexts):
|
||||
include_path = include_match.group("path")
|
||||
search_same_dir = include_match.group(1) == '"'
|
||||
found_included_path = self.amalgamation.find_included_file(
|
||||
include_path, self.file_dir if search_same_dir else None)
|
||||
if found_included_path:
|
||||
includes.append((include_match, found_included_path))
|
||||
|
||||
include_match = self.include_pattern.search(self.content,
|
||||
include_match.end())
|
||||
|
||||
# Handle all collected include directives.
|
||||
prev_end = 0
|
||||
tmp_content = ''
|
||||
for include in includes:
|
||||
include_match, found_included_path = include
|
||||
tmp_content += self.content[prev_end:include_match.start()]
|
||||
tmp_content += "// {0}\n".format(include_match.group(0))
|
||||
if found_included_path not in self.amalgamation.included_files:
|
||||
t = TranslationUnit(found_included_path, self.amalgamation, False)
|
||||
tmp_content += t.content
|
||||
prev_end = include_match.end()
|
||||
tmp_content += self.content[prev_end:]
|
||||
self.content = tmp_content
|
||||
|
||||
return len(includes)
|
||||
|
||||
# Make all content processing
|
||||
def _process(self):
|
||||
if not self.is_root:
|
||||
self._process_pragma_once()
|
||||
self._process_includes()
|
||||
|
||||
def __init__(self, file_path, amalgamation, is_root):
|
||||
self.file_path = file_path
|
||||
self.file_dir = os.path.dirname(file_path)
|
||||
self.amalgamation = amalgamation
|
||||
self.is_root = is_root
|
||||
|
||||
self.amalgamation.included_files.append(self.file_path)
|
||||
|
||||
actual_path = self.amalgamation.actual_path(file_path)
|
||||
if not os.path.isfile(actual_path):
|
||||
raise IOError("File not found: \"{0}\"".format(file_path))
|
||||
with open(actual_path, 'r') as f:
|
||||
self.content = f.read()
|
||||
self._process()
|
||||
|
||||
|
||||
def main():
|
||||
description = "Amalgamate C source and header files."
|
||||
usage = " ".join([
|
||||
"amalgamate.py",
|
||||
"[-v]",
|
||||
"-c path/to/config.json",
|
||||
"-s path/to/source/dir",
|
||||
"[-p path/to/prologue.(c|h)]"
|
||||
])
|
||||
argsparser = argparse.ArgumentParser(
|
||||
description=description, usage=usage)
|
||||
|
||||
argsparser.add_argument("-v", "--verbose", dest="verbose",
|
||||
choices=["yes", "no"], metavar="", help="be verbose")
|
||||
|
||||
argsparser.add_argument("-c", "--config", dest="config",
|
||||
required=True, metavar="", help="path to a JSON config file")
|
||||
|
||||
argsparser.add_argument("-s", "--source", dest="source_path",
|
||||
required=True, metavar="", help="source code path")
|
||||
|
||||
argsparser.add_argument("-p", "--prologue", dest="prologue",
|
||||
required=False, metavar="", help="path to a C prologue file")
|
||||
|
||||
amalgamation = Amalgamation(argsparser.parse_args())
|
||||
amalgamation.generate()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
8
scripts/config.json
Normal file
8
scripts/config.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"project": "entt",
|
||||
"target": "single_include/entt/entt.hpp",
|
||||
"sources": [
|
||||
"src/entt/entt.hpp"
|
||||
],
|
||||
"include_paths": ["src"]
|
||||
}
|
||||
60
scripts/update_homebrew.sh
Executable file
60
scripts/update_homebrew.sh
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/bin/sh
|
||||
|
||||
# only argument should be the version to upgrade to
|
||||
if [ $# != 1 ]
|
||||
then
|
||||
echo "Expected a version tag like v2.7.1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION="$1"
|
||||
URL="https://github.com/skypjack/entt/archive/$VERSION.tar.gz"
|
||||
FORMULA="entt.rb"
|
||||
|
||||
echo "Updating homebrew package to $VERSION"
|
||||
|
||||
echo "Cloning..."
|
||||
git clone https://github.com/skypjack/homebrew-entt.git
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
cd homebrew-entt
|
||||
|
||||
# download the repo at the version
|
||||
# exit with error messages if curl fails
|
||||
echo "Curling..."
|
||||
curl "$URL" --location --fail --silent --show-error --output archive.tar.gz
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# compute sha256 hash
|
||||
echo "Hashing..."
|
||||
HASH="$(openssl sha256 archive.tar.gz | cut -d " " -f 2)"
|
||||
|
||||
# delete the archive
|
||||
rm archive.tar.gz
|
||||
|
||||
echo "Sedding..."
|
||||
|
||||
# change the url in the formula file
|
||||
# the slashes in the URL must be escaped
|
||||
ESCAPED_URL="$(echo "$URL" | sed -e 's/[\/&]/\\&/g')"
|
||||
sed -i -e '/url/s/".*"/"'$ESCAPED_URL'"/' $FORMULA
|
||||
|
||||
# change the hash in the formula file
|
||||
sed -i -e '/sha256/s/".*"/"'$HASH'"/' $FORMULA
|
||||
|
||||
# delete temporary file created by sed
|
||||
rm -rf "$FORMULA-e"
|
||||
|
||||
# update remote repo
|
||||
echo "Gitting..."
|
||||
git add entt.rb
|
||||
git commit -m "Update to $VERSION"
|
||||
git push origin master
|
||||
|
||||
# out of homebrew-entt dir
|
||||
cd ..
|
||||
61437
single_include/entt/entt.hpp
Normal file
61437
single_include/entt/entt.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,235 +0,0 @@
|
||||
#ifndef ENTT_COMPONENT_POOL_HPP
|
||||
#define ENTT_COMPONENT_POOL_HPP
|
||||
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
template<typename, typename, typename...>
|
||||
class ComponentPool;
|
||||
|
||||
|
||||
template<typename Entity, typename Component>
|
||||
class ComponentPool<Entity, Component> {
|
||||
public:
|
||||
using component_type = Component;
|
||||
using entity_type = Entity;
|
||||
using pos_type = entity_type;
|
||||
using size_type = typename std::vector<component_type>::size_type;
|
||||
using iterator_type = typename std::vector<entity_type>::iterator;
|
||||
using const_iterator_type = typename std::vector<entity_type>::const_iterator;
|
||||
|
||||
private:
|
||||
inline bool valid(entity_type entity) const noexcept {
|
||||
return entity < reverse.size() && reverse[entity] < direct.size() && direct[reverse[entity]] == entity;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit ComponentPool(size_type dim = 4098) noexcept {
|
||||
assert(!(dim < 0));
|
||||
data.reserve(dim);
|
||||
}
|
||||
|
||||
ComponentPool(ComponentPool &&) = default;
|
||||
|
||||
~ComponentPool() noexcept {
|
||||
assert(empty());
|
||||
}
|
||||
|
||||
ComponentPool & operator=(ComponentPool &&) = default;
|
||||
|
||||
bool empty() const noexcept {
|
||||
return data.empty();
|
||||
}
|
||||
|
||||
size_type capacity() const noexcept {
|
||||
return data.capacity();
|
||||
}
|
||||
|
||||
size_type size() const noexcept {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
iterator_type begin() noexcept {
|
||||
return direct.begin();
|
||||
}
|
||||
|
||||
const_iterator_type cbegin() const noexcept {
|
||||
return direct.cbegin();
|
||||
}
|
||||
|
||||
iterator_type end() noexcept {
|
||||
return direct.end();
|
||||
}
|
||||
|
||||
const_iterator_type cend() const noexcept {
|
||||
return direct.cend();
|
||||
}
|
||||
|
||||
bool has(entity_type entity) const noexcept {
|
||||
return valid(entity);
|
||||
}
|
||||
|
||||
const component_type & get(entity_type entity) const noexcept {
|
||||
assert(valid(entity));
|
||||
return data[reverse[entity]];
|
||||
}
|
||||
|
||||
component_type & get(entity_type entity) noexcept {
|
||||
return const_cast<component_type &>(const_cast<const ComponentPool *>(this)->get(entity));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
component_type & construct(entity_type entity, Args... args) {
|
||||
assert(!valid(entity));
|
||||
|
||||
if(!(entity < reverse.size())) {
|
||||
reverse.resize(entity+1);
|
||||
}
|
||||
|
||||
reverse[entity] = pos_type(direct.size());
|
||||
direct.emplace_back(entity);
|
||||
data.push_back({ args... });
|
||||
|
||||
return data.back();
|
||||
}
|
||||
|
||||
void destroy(entity_type entity) {
|
||||
assert(valid(entity));
|
||||
|
||||
auto last = direct.size() - 1;
|
||||
|
||||
reverse[direct[last]] = reverse[entity];
|
||||
direct[reverse[entity]] = direct[last];
|
||||
data[reverse[entity]] = std::move(data[last]);
|
||||
|
||||
direct.pop_back();
|
||||
data.pop_back();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
data.clear();
|
||||
reverse.resize(0);
|
||||
direct.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<component_type> data;
|
||||
std::vector<pos_type> reverse;
|
||||
std::vector<entity_type> direct;
|
||||
};
|
||||
|
||||
|
||||
template<typename Entity, typename Component, typename... Components>
|
||||
class ComponentPool
|
||||
: ComponentPool<Entity, Component>, ComponentPool<Entity, Components>...
|
||||
{
|
||||
template<typename Comp>
|
||||
using Pool = ComponentPool<Entity, Comp>;
|
||||
|
||||
public:
|
||||
using entity_type = typename Pool<Component>::entity_type;
|
||||
using pos_type = typename Pool<Component>::pos_type;
|
||||
using size_type = typename Pool<Component>::size_type;
|
||||
using iterator_type = typename Pool<Component>::iterator_type;
|
||||
using const_iterator_type = typename Pool<Component>::const_iterator_type;
|
||||
|
||||
explicit ComponentPool(size_type dim = 4098) noexcept
|
||||
#ifdef _MSC_VER
|
||||
: ComponentPool<Entity, Component>{dim}, ComponentPool<Entity, Components>{dim}...
|
||||
#else
|
||||
: Pool<Component>{dim}, Pool<Components>{dim}...
|
||||
#endif
|
||||
{
|
||||
assert(!(dim < 0));
|
||||
}
|
||||
|
||||
ComponentPool(const ComponentPool &) = delete;
|
||||
ComponentPool(ComponentPool &&) = delete;
|
||||
|
||||
ComponentPool & operator=(const ComponentPool &) = delete;
|
||||
ComponentPool & operator=(ComponentPool &&) = delete;
|
||||
|
||||
template<typename Comp>
|
||||
bool empty() const noexcept {
|
||||
return Pool<Comp>::empty();
|
||||
}
|
||||
|
||||
template<typename Comp>
|
||||
size_type capacity() const noexcept {
|
||||
return Pool<Comp>::capacity();
|
||||
}
|
||||
|
||||
template<typename Comp>
|
||||
size_type size() const noexcept {
|
||||
return Pool<Comp>::size();
|
||||
}
|
||||
|
||||
template<typename Comp>
|
||||
iterator_type begin() noexcept {
|
||||
return Pool<Comp>::begin();
|
||||
}
|
||||
|
||||
template<typename Comp>
|
||||
const_iterator_type cbegin() const noexcept {
|
||||
return Pool<Comp>::cbegin();
|
||||
}
|
||||
|
||||
template<typename Comp>
|
||||
iterator_type end() noexcept {
|
||||
return Pool<Comp>::end();
|
||||
}
|
||||
|
||||
template<typename Comp>
|
||||
const_iterator_type cend() const noexcept {
|
||||
return Pool<Comp>::cend();
|
||||
}
|
||||
|
||||
template<typename Comp>
|
||||
bool has(entity_type entity) const noexcept {
|
||||
return Pool<Comp>::has(entity);
|
||||
}
|
||||
|
||||
template<typename Comp>
|
||||
const Comp & get(entity_type entity) const noexcept {
|
||||
return Pool<Comp>::get(entity);
|
||||
}
|
||||
|
||||
template<typename Comp>
|
||||
Comp & get(entity_type entity) noexcept {
|
||||
return const_cast<Comp &>(const_cast<const ComponentPool *>(this)->get<Comp>(entity));
|
||||
}
|
||||
|
||||
template<typename Comp, typename... Args>
|
||||
Comp & construct(entity_type entity, Args... args) {
|
||||
return Pool<Comp>::construct(entity, args...);
|
||||
}
|
||||
|
||||
template<typename Comp>
|
||||
void destroy(entity_type entity) {
|
||||
Pool<Comp>::destroy(entity);
|
||||
}
|
||||
|
||||
template<typename Comp>
|
||||
void reset() {
|
||||
Pool<Comp>::reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
using accumulator_type = int[];
|
||||
Pool<Component>::reset();
|
||||
accumulator_type accumulator = { (Pool<Components>::reset(), 0)... };
|
||||
(void)accumulator;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_COMPONENT_POOL_HPP
|
||||
69
src/entt/config/config.h
Normal file
69
src/entt/config/config.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#ifndef ENTT_CONFIG_CONFIG_H
|
||||
#define ENTT_CONFIG_CONFIG_H
|
||||
|
||||
#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
|
||||
# define ENTT_NOEXCEPT noexcept
|
||||
# define ENTT_THROW throw
|
||||
# define ENTT_TRY try
|
||||
# define ENTT_CATCH catch(...)
|
||||
#else
|
||||
# define ENTT_NOEXCEPT
|
||||
# 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
|
||||
# include <atomic>
|
||||
# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
|
||||
#endif
|
||||
|
||||
#ifndef ENTT_ID_TYPE
|
||||
# include <cstdint>
|
||||
# define ENTT_ID_TYPE std::uint32_t
|
||||
#endif
|
||||
|
||||
#ifndef ENTT_SPARSE_PAGE
|
||||
# define ENTT_SPARSE_PAGE 4096
|
||||
#endif
|
||||
|
||||
#ifndef ENTT_PACKED_PAGE
|
||||
# define ENTT_PACKED_PAGE 1024
|
||||
#endif
|
||||
|
||||
#ifdef ENTT_DISABLE_ASSERT
|
||||
# undef ENTT_ASSERT
|
||||
# define ENTT_ASSERT(...) (void(0))
|
||||
#elif !defined ENTT_ASSERT
|
||||
# include <cassert>
|
||||
# define ENTT_ASSERT(condition, ...) assert(condition)
|
||||
#endif
|
||||
|
||||
#ifdef ENTT_NO_ETO
|
||||
# define ENTT_IGNORE_IF_EMPTY false
|
||||
#else
|
||||
# define ENTT_IGNORE_IF_EMPTY true
|
||||
#endif
|
||||
|
||||
#ifndef ENTT_STANDARD_CPP
|
||||
# if defined __clang__ || defined __GNUC__
|
||||
# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
|
||||
# define ENTT_PRETTY_FUNCTION_PREFIX '='
|
||||
# define ENTT_PRETTY_FUNCTION_SUFFIX ']'
|
||||
# elif defined _MSC_VER
|
||||
# define ENTT_PRETTY_FUNCTION __FUNCSIG__
|
||||
# define ENTT_PRETTY_FUNCTION_PREFIX '<'
|
||||
# define ENTT_PRETTY_FUNCTION_SUFFIX '>'
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
8
src/entt/config/version.h
Normal file
8
src/entt/config/version.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef ENTT_CONFIG_VERSION_H
|
||||
#define ENTT_CONFIG_VERSION_H
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 9
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
|
||||
#endif
|
||||
959
src/entt/container/dense_hash_map.hpp
Normal file
959
src/entt/container/dense_hash_map.hpp
Normal file
@@ -0,0 +1,959 @@
|
||||
#ifndef ENTT_CONTAINER_DENSE_HASH_MAP_HPP
|
||||
#define ENTT_CONTAINER_DENSE_HASH_MAP_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/compressed_pair.hpp"
|
||||
#include "../core/memory.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Key, typename Type>
|
||||
struct dense_hash_map_node final {
|
||||
template<typename... Args>
|
||||
dense_hash_map_node(const std::size_t pos, Args &&...args)
|
||||
: next{pos},
|
||||
element{std::forward<Args>(args)...} {}
|
||||
|
||||
std::size_t next;
|
||||
std::pair<Key, Type> element;
|
||||
};
|
||||
|
||||
template<typename It>
|
||||
class dense_hash_map_iterator {
|
||||
friend dense_hash_map_iterator<const std::remove_pointer_t<It> *>;
|
||||
|
||||
using iterator_traits = std::iterator_traits<decltype(std::addressof(std::declval<It>()->element))>;
|
||||
|
||||
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 iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
dense_hash_map_iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
dense_hash_map_iterator(const It iter) ENTT_NOEXCEPT
|
||||
: it{iter} {}
|
||||
|
||||
template<bool Const = std::is_const_v<std::remove_pointer_t<It>>, typename = std::enable_if_t<Const>>
|
||||
dense_hash_map_iterator(const dense_hash_map_iterator<std::remove_const_t<std::remove_pointer_t<It>> *> &other)
|
||||
: it{other.it} {}
|
||||
|
||||
dense_hash_map_iterator &operator++() ENTT_NOEXCEPT {
|
||||
return ++it, *this;
|
||||
}
|
||||
|
||||
dense_hash_map_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
dense_hash_map_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
dense_hash_map_iterator &operator--() ENTT_NOEXCEPT {
|
||||
return --it, *this;
|
||||
}
|
||||
|
||||
dense_hash_map_iterator operator--(int) ENTT_NOEXCEPT {
|
||||
dense_hash_map_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
dense_hash_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
|
||||
it += value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
dense_hash_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
||||
dense_hash_map_iterator copy = *this;
|
||||
return (copy += value);
|
||||
}
|
||||
|
||||
dense_hash_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
|
||||
return (*this += -value);
|
||||
}
|
||||
|
||||
dense_hash_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return (*this + -value);
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator[](const difference_type value) const {
|
||||
return it[value].element;
|
||||
}
|
||||
|
||||
[[nodiscard]] pointer operator->() const {
|
||||
return std::addressof(it->element);
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend auto operator-(const dense_hash_map_iterator<ILhs> &, const dense_hash_map_iterator<IRhs> &) ENTT_NOEXCEPT;
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend bool operator==(const dense_hash_map_iterator<ILhs> &, const dense_hash_map_iterator<IRhs> &) ENTT_NOEXCEPT;
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend bool operator<(const dense_hash_map_iterator<ILhs> &, const dense_hash_map_iterator<IRhs> &) ENTT_NOEXCEPT;
|
||||
|
||||
private:
|
||||
It it;
|
||||
};
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] auto operator-(const dense_hash_map_iterator<ILhs> &lhs, const dense_hash_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.it - rhs.it;
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator==(const dense_hash_map_iterator<ILhs> &lhs, const dense_hash_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.it == rhs.it;
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator!=(const dense_hash_map_iterator<ILhs> &lhs, const dense_hash_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator<(const dense_hash_map_iterator<ILhs> &lhs, const dense_hash_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.it < rhs.it;
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator>(const dense_hash_map_iterator<ILhs> &lhs, const dense_hash_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator<=(const dense_hash_map_iterator<ILhs> &lhs, const dense_hash_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs > rhs);
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator>=(const dense_hash_map_iterator<ILhs> &lhs, const dense_hash_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
class dense_hash_map_local_iterator {
|
||||
friend dense_hash_map_local_iterator<const std::remove_pointer_t<It> *>;
|
||||
|
||||
using iterator_traits = std::iterator_traits<decltype(std::addressof(std::declval<It>()->element))>;
|
||||
|
||||
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 iterator_category = std::forward_iterator_tag;
|
||||
|
||||
dense_hash_map_local_iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
dense_hash_map_local_iterator(It iter, const std::size_t pos) ENTT_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_map_local_iterator(const dense_hash_map_local_iterator<std::remove_const_t<std::remove_pointer_t<It>> *> &other)
|
||||
: it{other.it},
|
||||
offset{other.offset} {}
|
||||
|
||||
dense_hash_map_local_iterator &operator++() ENTT_NOEXCEPT {
|
||||
return offset = it[offset].next, *this;
|
||||
}
|
||||
|
||||
dense_hash_map_local_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
dense_hash_map_local_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] pointer operator->() const {
|
||||
return std::addressof(it[offset].element);
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT {
|
||||
return offset;
|
||||
}
|
||||
|
||||
private:
|
||||
It it;
|
||||
std::size_t offset;
|
||||
};
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator==(const dense_hash_map_local_iterator<ILhs> &lhs, const dense_hash_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.index() == rhs.index();
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator!=(const dense_hash_map_local_iterator<ILhs> &lhs, const dense_hash_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Associative container for key-value pairs with unique keys.
|
||||
*
|
||||
* Internally, elements are organized into buckets. Which bucket an element is
|
||||
* placed into depends entirely on the hash of its key. Keys with the same hash
|
||||
* code appear in the same bucket.
|
||||
*
|
||||
* @tparam Key Key type of the associative container.
|
||||
* @tparam Type Mapped type of the associative container.
|
||||
* @tparam Hash Type of function to use to hash the keys.
|
||||
* @tparam KeyEqual Type of function to use to compare the keys for equality.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Key, typename Type, typename Hash, typename KeyEqual, typename Allocator>
|
||||
class dense_hash_map final {
|
||||
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<std::pair<const Key, Type>>;
|
||||
using alloc_traits = typename std::allocator_traits<alloc>;
|
||||
|
||||
using node_type = internal::dense_hash_map_node<const Key, 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]] auto constrained_find(const Other &key, std::size_t bucket) {
|
||||
for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
|
||||
if(packed.second()(it->first, key)) {
|
||||
return begin() + it.index();
|
||||
}
|
||||
}
|
||||
|
||||
return end();
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const {
|
||||
for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
|
||||
if(packed.second()(it->first, key)) {
|
||||
return cbegin() + it.index();
|
||||
}
|
||||
}
|
||||
|
||||
return cend();
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
[[nodiscard]] auto get_or_emplace(const Key &key, Args &&...args) {
|
||||
const auto hash = sparse.second()(key);
|
||||
auto index = hash_to_bucket(hash);
|
||||
|
||||
if(auto it = constrained_find(key, 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<Args>(args)...);
|
||||
// update goes after emplace to enforce exception guarantees
|
||||
sparse.first()[index] = size() - 1u;
|
||||
|
||||
return std::make_pair(--end(), true);
|
||||
}
|
||||
|
||||
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.first);
|
||||
for(; *curr != last; curr = &packed.first()[*curr].next) {}
|
||||
*curr = pos;
|
||||
|
||||
using node_allocator_traits = typename alloc_traits::template rebind_traits<decltype(node_type::element)>;
|
||||
typename node_allocator_traits::allocator_type allocator = packed.first().get_allocator();
|
||||
auto *ptr = std::addressof(packed.first()[pos].element);
|
||||
|
||||
std::destroy_at(ptr);
|
||||
packed.first()[pos].next = packed.first().back().next;
|
||||
// no exception guarantees when mapped type has a throwing move constructor (we're technically doomed)
|
||||
node_allocator_traits::construct(allocator, ptr, std::move(packed.first().back().element));
|
||||
}
|
||||
|
||||
packed.first().pop_back();
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Key type of the container. */
|
||||
using key_type = Key;
|
||||
/*! @brief Mapped type of the container. */
|
||||
using mapped_type = Type;
|
||||
/*! @brief Key-value type of the container. */
|
||||
using value_type = std::pair<const Key, Type>;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Type of function to use to hash the keys. */
|
||||
using hasher = Hash;
|
||||
/*! @brief Type of function to use to compare the keys for equality. */
|
||||
using key_equal = KeyEqual;
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = internal::dense_hash_map_iterator<typename packed_container_type::pointer>;
|
||||
/*! @brief Constant random access iterator type. */
|
||||
using const_iterator = internal::dense_hash_map_iterator<typename packed_container_type::const_pointer>;
|
||||
/*! @brief Forward iterator type. */
|
||||
using local_iterator = internal::dense_hash_map_local_iterator<typename packed_container_type::pointer>;
|
||||
/*! @brief Constant forward iterator type. */
|
||||
using const_local_iterator = internal::dense_hash_map_local_iterator<typename packed_container_type::const_pointer>;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
dense_hash_map()
|
||||
: dense_hash_map(minimum_capacity) {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit dense_hash_map(const allocator_type &allocator)
|
||||
: dense_hash_map{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 allocator The allocator to use.
|
||||
*/
|
||||
dense_hash_map(const size_type bucket_count, const allocator_type &allocator)
|
||||
: dense_hash_map{bucket_count, 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 hash Hash function to use.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
dense_hash_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator)
|
||||
: dense_hash_map{bucket_count, 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 hash Hash function to use.
|
||||
* @param equal Compare function to use.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit dense_hash_map(const size_type bucket_count, 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
*/
|
||||
dense_hash_map(const dense_hash_map &other)
|
||||
: dense_hash_map{other, alloc_traits::select_on_container_copy_construction(other.get_allocator())} {}
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
dense_hash_map(const dense_hash_map &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} {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
dense_hash_map(dense_hash_map &&other) ENTT_NOEXCEPT = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
dense_hash_map(dense_hash_map &&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())},
|
||||
threshold{other.threshold} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~dense_hash_map() = default;
|
||||
|
||||
/**
|
||||
* @brief Copy assignment operator.
|
||||
* @param other The instance to copy from.
|
||||
* @return This container.
|
||||
*/
|
||||
dense_hash_map &operator=(const dense_hash_map &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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This container.
|
||||
*/
|
||||
dense_hash_map &operator=(dense_hash_map &&other) ENTT_NOEXCEPT = default;
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_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();
|
||||
}
|
||||
|
||||
/*! @copydoc cbegin */
|
||||
[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/*! @copydoc begin */
|
||||
[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
|
||||
return packed.first().data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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();
|
||||
}
|
||||
|
||||
/*! @copydoc cend */
|
||||
[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/*! @copydoc end */
|
||||
[[nodiscard]] iterator end() ENTT_NOEXCEPT {
|
||||
return packed.first().data() + size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a container is empty.
|
||||
* @return True if the container is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return packed.first().empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements in a container.
|
||||
* @return Number of elements in a container.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
|
||||
return packed.first().size();
|
||||
}
|
||||
|
||||
/*! @brief Clears the container. */
|
||||
void clear() ENTT_NOEXCEPT {
|
||||
sparse.first().clear();
|
||||
packed.first().clear();
|
||||
rehash(0u);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts an element into the container, if the key does not exist.
|
||||
* @param value A key-value pair eventually convertible to the value type.
|
||||
* @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<iterator, bool> insert(const value_type &value) {
|
||||
return emplace(value);
|
||||
}
|
||||
|
||||
/*! @copydoc insert */
|
||||
std::pair<iterator, bool> insert(value_type &&value) {
|
||||
return emplace(std::move(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc insert
|
||||
* @tparam Arg Type of the key-value pair to insert into the container.
|
||||
*/
|
||||
template<typename Arg>
|
||||
std::enable_if_t<std::is_constructible_v<value_type, Arg &&>, std::pair<iterator, bool>>
|
||||
insert(Arg &&value) {
|
||||
return emplace(std::forward<Arg>(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts elements into the container, if their keys do not exist.
|
||||
* @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.
|
||||
*/
|
||||
template<typename It>
|
||||
void insert(It first, It last) {
|
||||
for(; first != last; ++first) {
|
||||
emplace(*first);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts an element into the container or assigns to the current
|
||||
* element if the key already exists.
|
||||
* @tparam Arg Type of the value to insert or assign.
|
||||
* @param key A key used both to look up and to insert if not found.
|
||||
* @param value A value to insert or assign.
|
||||
* @return A pair consisting of an iterator to the element and a bool
|
||||
* denoting whether the insertion took place.
|
||||
*/
|
||||
template<typename Arg>
|
||||
std::pair<iterator, bool> insert_or_assign(const key_type &key, Arg &&value) {
|
||||
auto result = try_emplace(key, std::forward<Arg>(value));
|
||||
|
||||
if(!result.second) {
|
||||
result.first->second = std::forward<Arg>(value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*! @copydoc insert_or_assign */
|
||||
template<typename Arg>
|
||||
std::pair<iterator, bool> insert_or_assign(key_type &&key, Arg &&value) {
|
||||
auto result = try_emplace(std::move(key), std::forward<Arg>(value));
|
||||
|
||||
if(!result.second) {
|
||||
result.first->second = std::forward<Arg>(value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs an element in-place, if the key does not exist.
|
||||
* @tparam Args Types of arguments to forward to the constructor of the
|
||||
* element.
|
||||
* @param args Arguments to forward to the constructor of the element.
|
||||
* @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.
|
||||
*/
|
||||
template<typename... Args>
|
||||
std::pair<iterator, bool> emplace(Args &&...args) {
|
||||
if constexpr(sizeof...(Args) == 0u) {
|
||||
return get_or_emplace(key_type{});
|
||||
} else if constexpr(sizeof...(Args) == 1u) {
|
||||
return get_or_emplace(args.first..., std::forward<Args>(args)...);
|
||||
} else if constexpr(sizeof...(Args) == 2u) {
|
||||
return get_or_emplace(std::get<0u>(std::tie(args...)), std::forward<Args>(args)...);
|
||||
} else {
|
||||
static_assert(sizeof...(Args) == 3u, "Invalid arguments");
|
||||
return emplace(std::pair<key_type, mapped_type>{std::forward<Args>(args)...});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts in-place if the key does not exist, does nothing if the
|
||||
* key exists.
|
||||
* @tparam Args Types of arguments to forward to the constructor of the
|
||||
* element.
|
||||
* @param key A key used both to look up and to insert if not found.
|
||||
* @param args Arguments to forward to the constructor of the element.
|
||||
* @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.
|
||||
*/
|
||||
template<typename... Args>
|
||||
std::pair<iterator, bool> try_emplace(const key_type &key, Args &&...args) {
|
||||
return get_or_emplace(key, std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
/*! @copydoc try_emplace */
|
||||
template<typename... Args>
|
||||
std::pair<iterator, bool> try_emplace(key_type &&key, Args &&...args) {
|
||||
return get_or_emplace(key, std::piecewise_construct, std::forward_as_tuple(std::move(key)), std::forward_as_tuple(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes an element from a given position.
|
||||
* @param pos An iterator to the element to remove.
|
||||
* @return An iterator following the removed element.
|
||||
*/
|
||||
iterator erase(const_iterator pos) {
|
||||
const auto dist = std::distance(cbegin(), pos);
|
||||
erase(pos->first);
|
||||
return begin() + dist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the given elements from a container.
|
||||
* @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 An iterator following the last removed element.
|
||||
*/
|
||||
iterator erase(const_iterator first, const_iterator last) {
|
||||
const auto dist = std::distance(cbegin(), first);
|
||||
|
||||
for(auto rfirst = std::make_reverse_iterator(last), rlast = std::make_reverse_iterator(first); rfirst != rlast; ++rfirst) {
|
||||
erase(rfirst->first);
|
||||
}
|
||||
|
||||
return dist > static_cast<decltype(dist)>(size()) ? end() : (begin() + dist);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the element associated with a given key.
|
||||
* @param key A key value of an element to remove.
|
||||
* @return Number of elements removed (either 0 or 1).
|
||||
*/
|
||||
size_type erase(const key_type &key) {
|
||||
for(size_type *curr = sparse.first().data() + bucket(key); *curr != std::numeric_limits<size_type>::max(); curr = &packed.first()[*curr].next) {
|
||||
if(packed.second()(packed.first()[*curr].element.first, key)) {
|
||||
const auto index = *curr;
|
||||
*curr = packed.first()[*curr].next;
|
||||
move_and_pop(index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given container.
|
||||
* @param other Container to exchange the content with.
|
||||
*/
|
||||
void swap(dense_hash_map &other) {
|
||||
using std::swap;
|
||||
swap(sparse, other.sparse);
|
||||
swap(packed, other.packed);
|
||||
swap(threshold, other.threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Accesses a given element with bounds checking.
|
||||
* @param key A key of an element to find.
|
||||
* @return A reference to the mapped value of the requested element.
|
||||
*/
|
||||
[[nodiscard]] Type &at(const key_type &key) {
|
||||
auto it = find(key);
|
||||
ENTT_ASSERT(it != end(), "Invalid key");
|
||||
return it->second;
|
||||
}
|
||||
|
||||
/*! @copydoc at */
|
||||
[[nodiscard]] const Type &at(const key_type &key) const {
|
||||
auto it = find(key);
|
||||
ENTT_ASSERT(it != cend(), "Invalid key");
|
||||
return it->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Accesses or inserts a given element.
|
||||
* @param key A key of an element to find or insert.
|
||||
* @return A reference to the mapped value of the requested element.
|
||||
*/
|
||||
[[nodiscard]] Type &operator[](const key_type &key) {
|
||||
return try_emplace(key).first->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Accesses or inserts a given element.
|
||||
* @param key A key of an element to find or insert.
|
||||
* @return A reference to the mapped value of the requested element.
|
||||
*/
|
||||
[[nodiscard]] Type &operator[](key_type &&key) {
|
||||
return try_emplace(std::move(key)).first->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds an element with a given key.
|
||||
* @param key Key value of an element to search for.
|
||||
* @return An iterator to an element with the given key. If no such element
|
||||
* is found, a past-the-end iterator is returned.
|
||||
*/
|
||||
[[nodiscard]] iterator find(const key_type &key) {
|
||||
return constrained_find(key, bucket(key));
|
||||
}
|
||||
|
||||
/*! @copydoc find */
|
||||
[[nodiscard]] const_iterator find(const key_type &key) const {
|
||||
return constrained_find(key, bucket(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds an element with a key that compares _equivalent_ to a given
|
||||
* value.
|
||||
* @tparam Other Type of the key value of an element to search for.
|
||||
* @param key Key value of an element to search for.
|
||||
* @return An iterator to an element with the given key. If no such element
|
||||
* is found, a past-the-end iterator is returned.
|
||||
*/
|
||||
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 &key) {
|
||||
return constrained_find(key, bucket(key));
|
||||
}
|
||||
|
||||
/*! @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 &key) const {
|
||||
return constrained_find(key, bucket(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the container contains an element with a given key.
|
||||
* @param key Key value of an element to search for.
|
||||
* @return True if there is such an element, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(const key_type &key) const {
|
||||
return (find(key) != cend());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the container contains an element with a key that
|
||||
* compares _equivalent_ to a given value.
|
||||
* @tparam Other Type of the key value of an element to search for.
|
||||
* @param key Key value of an element to search for.
|
||||
* @return True if there is such an element, false otherwise.
|
||||
*/
|
||||
template<typename Other>
|
||||
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>>
|
||||
contains(const Other &key) const {
|
||||
return (find(key) != cend());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning of a given bucket.
|
||||
* @param index An index of a bucket to access.
|
||||
* @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]};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning of a given bucket.
|
||||
* @param index An index of a bucket to access.
|
||||
* @return An iterator to the beginning of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] const_local_iterator begin(const size_type index) const {
|
||||
return cbegin(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning of a given bucket.
|
||||
* @param index An index of a bucket to access.
|
||||
* @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]};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end of a given bucket.
|
||||
* @param index An index of a bucket to access.
|
||||
* @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()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end of a given bucket.
|
||||
* @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 {
|
||||
return cend(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end of a given bucket.
|
||||
* @param index An index of a bucket to access.
|
||||
* @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()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of buckets.
|
||||
* @return The number of buckets.
|
||||
*/
|
||||
[[nodiscard]] size_type bucket_count() const {
|
||||
return sparse.first().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the maximum number of buckets.
|
||||
* @return The maximum number of buckets.
|
||||
*/
|
||||
[[nodiscard]] size_type max_bucket_count() const {
|
||||
return sparse.first().max_size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements in a given bucket.
|
||||
* @param index The index of the bucket to examine.
|
||||
* @return The number of elements in the given bucket.
|
||||
*/
|
||||
[[nodiscard]] size_type bucket_size(const size_type index) const {
|
||||
return static_cast<size_type>(std::distance(begin(index), end(index)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the bucket for a given key.
|
||||
* @param key The value of the key to examine.
|
||||
* @return The bucket for the given key.
|
||||
*/
|
||||
[[nodiscard]] size_type bucket(const key_type &key) const {
|
||||
return hash_to_bucket(sparse.second()(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the average number of elements per bucket.
|
||||
* @return The average number of elements per bucket.
|
||||
*/
|
||||
[[nodiscard]] float load_factor() const {
|
||||
return size() / static_cast<float>(bucket_count());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the maximum average number of elements per bucket.
|
||||
* @return The maximum average number of elements per bucket.
|
||||
*/
|
||||
[[nodiscard]] float max_load_factor() const {
|
||||
return threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the desired maximum average number of elements per bucket.
|
||||
* @param value A desired maximum average number of elements per bucket.
|
||||
*/
|
||||
void max_load_factor(const float value) {
|
||||
ENTT_ASSERT(value > 0.f, "Invalid load factor");
|
||||
threshold = value;
|
||||
rehash(0u);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reserves at least the specified number of buckets and regenerates
|
||||
* the hash table.
|
||||
* @param count 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()));
|
||||
|
||||
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(size_type pos{}, last = size(); pos < last; ++pos) {
|
||||
const auto index = bucket(packed.first()[pos].element.first);
|
||||
packed.first()[pos].next = std::exchange(sparse.first()[index], pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reserves space for at least the specified number of elements and
|
||||
* regenerates the hash table.
|
||||
* @param count 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())));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the function used to hash the keys.
|
||||
* @return The function used to hash the keys.
|
||||
*/
|
||||
[[nodiscard]] hasher hash_function() const {
|
||||
return sparse.second();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the function used to compare keys for equality.
|
||||
* @return The function used to compare keys for equality.
|
||||
*/
|
||||
[[nodiscard]] key_equal key_eq() const {
|
||||
return packed.second();
|
||||
}
|
||||
|
||||
private:
|
||||
compressed_pair<sparse_container_type, hasher> sparse;
|
||||
compressed_pair<packed_container_type, key_equal> packed;
|
||||
float threshold;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
849
src/entt/container/dense_hash_set.hpp
Normal file
849
src/entt/container/dense_hash_set.hpp
Normal file
@@ -0,0 +1,849 @@
|
||||
#ifndef ENTT_CONTAINER_DENSE_HASH_SET_HPP
|
||||
#define ENTT_CONTAINER_DENSE_HASH_SET_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/compressed_pair.hpp"
|
||||
#include "../core/memory.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
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)))>;
|
||||
|
||||
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 iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
dense_hash_set_iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
dense_hash_set_iterator(const It iter) ENTT_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)
|
||||
: it{other.it} {}
|
||||
|
||||
dense_hash_set_iterator &operator++() ENTT_NOEXCEPT {
|
||||
return ++it, *this;
|
||||
}
|
||||
|
||||
dense_hash_set_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
dense_hash_set_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
dense_hash_set_iterator &operator--() ENTT_NOEXCEPT {
|
||||
return --it, *this;
|
||||
}
|
||||
|
||||
dense_hash_set_iterator operator--(int) ENTT_NOEXCEPT {
|
||||
dense_hash_set_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
dense_hash_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
|
||||
it += value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
dense_hash_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
||||
dense_hash_set_iterator copy = *this;
|
||||
return (copy += value);
|
||||
}
|
||||
|
||||
dense_hash_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
|
||||
return (*this += -value);
|
||||
}
|
||||
|
||||
dense_hash_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return (*this + -value);
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator[](const difference_type value) const {
|
||||
return it[value].element;
|
||||
}
|
||||
|
||||
[[nodiscard]] pointer operator->() const {
|
||||
return std::addressof(it->element);
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const {
|
||||
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 ILhs, typename IRhs>
|
||||
friend bool operator==(const dense_hash_set_iterator<ILhs> &, const dense_hash_set_iterator<IRhs> &) ENTT_NOEXCEPT;
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend bool operator<(const dense_hash_set_iterator<ILhs> &, const dense_hash_set_iterator<IRhs> &) ENTT_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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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)))>;
|
||||
|
||||
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 iterator_category = std::forward_iterator_tag;
|
||||
|
||||
dense_hash_set_local_iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
dense_hash_set_local_iterator(It iter, const std::size_t pos) ENTT_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)
|
||||
: it{other.it},
|
||||
offset{other.offset} {}
|
||||
|
||||
dense_hash_set_local_iterator &operator++() ENTT_NOEXCEPT {
|
||||
return offset = it[offset].next, *this;
|
||||
}
|
||||
|
||||
dense_hash_set_local_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
dense_hash_set_local_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] pointer operator->() const {
|
||||
return std::addressof(it[offset].element);
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT {
|
||||
return offset;
|
||||
}
|
||||
|
||||
private:
|
||||
It it;
|
||||
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 {
|
||||
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 {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Associative container for unique objects of a given type.
|
||||
*
|
||||
* Internally, elements are organized into buckets. Which bucket an element is
|
||||
* placed into depends entirely on its hash. Elements with the same hash code
|
||||
* appear in the same bucket.
|
||||
*
|
||||
* @tparam Type Value type of the associative container.
|
||||
* @tparam Hash Type of function to use to hash the values.
|
||||
* @tparam KeyEqual Type of function to use to compare the values for equality.
|
||||
* @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 {
|
||||
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 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]] 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 end();
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const {
|
||||
for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
|
||||
if(packed.second()(*it, value)) {
|
||||
return cbegin() + 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);
|
||||
|
||||
if(auto it = constrained_find(arg, 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;
|
||||
|
||||
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
|
||||
packed.first()[pos] = std::move(packed.first().back());
|
||||
}
|
||||
|
||||
packed.first().pop_back();
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Key type of the container. */
|
||||
using key_type = Type;
|
||||
/*! @brief Value type of the container. */
|
||||
using value_type = Type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Type of function to use to hash the elements. */
|
||||
using hasher = Hash;
|
||||
/*! @brief Type of function to use to compare the elements for equality. */
|
||||
using key_equal = KeyEqual;
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = internal::dense_hash_set_iterator<typename packed_container_type::pointer>;
|
||||
/*! @brief Constant random access iterator type. */
|
||||
using const_iterator = internal::dense_hash_set_iterator<typename packed_container_type::const_pointer>;
|
||||
/*! @brief Forward iterator type. */
|
||||
using local_iterator = internal::dense_hash_set_local_iterator<typename packed_container_type::pointer>;
|
||||
/*! @brief Constant forward iterator type. */
|
||||
using const_local_iterator = internal::dense_hash_set_local_iterator<typename packed_container_type::const_pointer>;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
dense_hash_set()
|
||||
: dense_hash_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} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator and user
|
||||
* supplied minimal number of buckets.
|
||||
* @param bucket_count 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} {}
|
||||
|
||||
/**
|
||||
* @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 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} {}
|
||||
|
||||
/**
|
||||
* @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 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())
|
||||
: sparse{allocator, hash},
|
||||
packed{allocator, equal},
|
||||
threshold{default_threshold} {
|
||||
rehash(bucket_count);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 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} {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
dense_hash_set(dense_hash_set &&other) ENTT_NOEXCEPT = 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())},
|
||||
threshold{other.threshold} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~dense_hash_set() = default;
|
||||
|
||||
/**
|
||||
* @brief Copy assignment operator.
|
||||
* @param other The instance to copy from.
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_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();
|
||||
}
|
||||
|
||||
/*! @copydoc cbegin */
|
||||
[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/*! @copydoc begin */
|
||||
[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
|
||||
return packed.first().data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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();
|
||||
}
|
||||
|
||||
/*! @copydoc cend */
|
||||
[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/*! @copydoc end */
|
||||
[[nodiscard]] iterator end() ENTT_NOEXCEPT {
|
||||
return packed.first().data() + size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a container is empty.
|
||||
* @return True if the container is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return packed.first().empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements in a container.
|
||||
* @return Number of elements in a container.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
|
||||
return packed.first().size();
|
||||
}
|
||||
|
||||
/*! @brief Clears the container. */
|
||||
void clear() ENTT_NOEXCEPT {
|
||||
sparse.first().clear();
|
||||
packed.first().clear();
|
||||
rehash(0u);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts an element into the container, if it does not exist.
|
||||
* @param value An element to insert into the container.
|
||||
* @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<iterator, bool> insert(const value_type &value) {
|
||||
return emplace(value);
|
||||
}
|
||||
|
||||
/*! @copydoc insert */
|
||||
std::pair<iterator, bool> insert(value_type &&value) {
|
||||
return emplace(std::move(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts elements into the container, if they do not exist.
|
||||
* @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.
|
||||
*/
|
||||
template<typename It>
|
||||
void insert(It first, It last) {
|
||||
for(; first != last; ++first) {
|
||||
emplace(*first);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs an element in-place, if it does not exist.
|
||||
* @tparam Args Types of arguments to forward to the constructor of the
|
||||
* element.
|
||||
* @param args Arguments to forward to the constructor of the element.
|
||||
* @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.
|
||||
*/
|
||||
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)...);
|
||||
} else {
|
||||
return get_or_emplace(value_type{std::forward<Args>(args)...});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes an element from a given position.
|
||||
* @param pos An iterator to the element to remove.
|
||||
* @return An iterator following the removed element.
|
||||
*/
|
||||
iterator erase(const_iterator pos) {
|
||||
const auto dist = std::distance(cbegin(), pos);
|
||||
erase(*pos);
|
||||
return begin() + dist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the given elements from a container.
|
||||
* @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 An iterator following the last removed element.
|
||||
*/
|
||||
iterator erase(const_iterator first, const_iterator last) {
|
||||
const auto dist = std::distance(cbegin(), first);
|
||||
|
||||
for(auto rfirst = std::make_reverse_iterator(last), rlast = std::make_reverse_iterator(first); rfirst != rlast; ++rfirst) {
|
||||
erase(*rfirst);
|
||||
}
|
||||
|
||||
return dist > static_cast<decltype(dist)>(size()) ? end() : (begin() + dist);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the element associated with a given value.
|
||||
* @param value Value of an element to remove.
|
||||
* @return Number of elements removed (either 0 or 1).
|
||||
*/
|
||||
size_type erase(const value_type &value) {
|
||||
return do_erase(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given container.
|
||||
* @param other Container to exchange the content with.
|
||||
*/
|
||||
void swap(dense_hash_set &other) {
|
||||
using std::swap;
|
||||
swap(sparse, other.sparse);
|
||||
swap(packed, other.packed);
|
||||
swap(threshold, other.threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds an element with a given value.
|
||||
* @param value Value of an element to search for.
|
||||
* @return An iterator to an element with the given value. If no such
|
||||
* element is found, a past-the-end iterator is returned.
|
||||
*/
|
||||
[[nodiscard]] iterator find(const value_type &value) {
|
||||
return constrained_find(value, bucket(value));
|
||||
}
|
||||
|
||||
/*! @copydoc find */
|
||||
[[nodiscard]] const_iterator find(const value_type &value) const {
|
||||
return constrained_find(value, bucket(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds an element that compares _equivalent_ to a given value.
|
||||
* @tparam Other Type of an element to search for.
|
||||
* @param value Value of an element to search for.
|
||||
* @return An iterator to an element with the given value. If no such
|
||||
* element is found, a past-the-end iterator is returned.
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
/*! @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));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the container contains an element with a given value.
|
||||
* @param value Value of an element to search for.
|
||||
* @return True if there is such an element, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(const value_type &value) const {
|
||||
return (find(value) != cend());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the container contains an element that compares
|
||||
* _equivalent_ to a given value.
|
||||
* @tparam Other Type of an element to search for.
|
||||
* @param value Value of an element to search for.
|
||||
* @return True if there is such an element, false otherwise.
|
||||
*/
|
||||
template<typename Other>
|
||||
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>>
|
||||
contains(const Other &value) const {
|
||||
return (find(value) != cend());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning of a given bucket.
|
||||
* @param index An index of a bucket to access.
|
||||
* @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]};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning of a given bucket.
|
||||
* @param index An index of a bucket to access.
|
||||
* @return An iterator to the beginning of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] const_local_iterator begin(const size_type index) const {
|
||||
return cbegin(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning of a given bucket.
|
||||
* @param index An index of a bucket to access.
|
||||
* @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]};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end of a given bucket.
|
||||
* @param index An index of a bucket to access.
|
||||
* @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()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end of a given bucket.
|
||||
* @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 {
|
||||
return cend(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end of a given bucket.
|
||||
* @param index An index of a bucket to access.
|
||||
* @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()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of buckets.
|
||||
* @return The number of buckets.
|
||||
*/
|
||||
[[nodiscard]] size_type bucket_count() const {
|
||||
return sparse.first().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the maximum number of buckets.
|
||||
* @return The maximum number of buckets.
|
||||
*/
|
||||
[[nodiscard]] size_type max_bucket_count() const {
|
||||
return sparse.first().max_size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements in a given bucket.
|
||||
* @param index The index of the bucket to examine.
|
||||
* @return The number of elements in the given bucket.
|
||||
*/
|
||||
[[nodiscard]] size_type bucket_size(const size_type index) const {
|
||||
return static_cast<size_type>(std::distance(begin(index), end(index)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the bucket for a given element.
|
||||
* @param value The value of the element to examine.
|
||||
* @return The bucket for the given element.
|
||||
*/
|
||||
[[nodiscard]] size_type bucket(const value_type &value) const {
|
||||
return hash_to_bucket(sparse.second()(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the average number of elements per bucket.
|
||||
* @return The average number of elements per bucket.
|
||||
*/
|
||||
[[nodiscard]] float load_factor() const {
|
||||
return size() / static_cast<float>(bucket_count());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the maximum average number of elements per bucket.
|
||||
* @return The maximum average number of elements per bucket.
|
||||
*/
|
||||
[[nodiscard]] float max_load_factor() const {
|
||||
return threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the desired maximum average number of elements per bucket.
|
||||
* @param value A desired maximum average number of elements per bucket.
|
||||
*/
|
||||
void max_load_factor(const float value) {
|
||||
ENTT_ASSERT(value > 0.f, "Invalid load factor");
|
||||
threshold = value;
|
||||
rehash(0u);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reserves at least the specified number of buckets and regenerates
|
||||
* the hash table.
|
||||
* @param count 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()));
|
||||
|
||||
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(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reserves space for at least the specified number of elements and
|
||||
* regenerates the hash table.
|
||||
* @param count 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())));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the function used to hash the elements.
|
||||
* @return The function used to hash the elements.
|
||||
*/
|
||||
[[nodiscard]] hasher hash_function() const {
|
||||
return sparse.second();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the function used to compare elements for equality.
|
||||
* @return The function used to compare elements for equality.
|
||||
*/
|
||||
[[nodiscard]] key_equal key_eq() const {
|
||||
return packed.second();
|
||||
}
|
||||
|
||||
private:
|
||||
compressed_pair<sparse_container_type, hasher> sparse;
|
||||
compressed_pair<packed_container_type, key_equal> packed;
|
||||
float threshold;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
25
src/entt/container/fwd.hpp
Normal file
25
src/entt/container/fwd.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef ENTT_CONTAINER_FWD_HPP
|
||||
#define ENTT_CONTAINER_FWD_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace entt {
|
||||
|
||||
template<
|
||||
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;
|
||||
|
||||
template<
|
||||
typename Type,
|
||||
typename = std::hash<Type>,
|
||||
typename = std::equal_to<Type>,
|
||||
typename = std::allocator<Type>>
|
||||
class dense_hash_set;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
137
src/entt/core/algorithm.hpp
Normal file
137
src/entt/core/algorithm.hpp
Normal file
@@ -0,0 +1,137 @@
|
||||
#ifndef ENTT_CORE_ALGORITHM_HPP
|
||||
#define ENTT_CORE_ALGORITHM_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Function object to wrap `std::sort` in a class type.
|
||||
*
|
||||
* Unfortunately, `std::sort` cannot be passed as template argument to a class
|
||||
* template or a function template.<br/>
|
||||
* This class fills the gap by wrapping some flavors of `std::sort` in a
|
||||
* function object.
|
||||
*/
|
||||
struct std_sort {
|
||||
/**
|
||||
* @brief Sorts the elements in a range.
|
||||
*
|
||||
* Sorts the elements in a range using the given binary comparison function.
|
||||
*
|
||||
* @tparam It Type of random access iterator.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function.
|
||||
* @param first An iterator to the first element of the range to sort.
|
||||
* @param last An iterator past the last element of the range to sort.
|
||||
* @param compare A valid comparison function object.
|
||||
* @param args Arguments to forward to the sort function, if any.
|
||||
*/
|
||||
template<typename It, typename Compare = std::less<>, typename... Args>
|
||||
void operator()(It first, It last, Compare compare = Compare{}, Args &&...args) const {
|
||||
std::sort(std::forward<Args>(args)..., std::move(first), std::move(last), std::move(compare));
|
||||
}
|
||||
};
|
||||
|
||||
/*! @brief Function object for performing insertion sort. */
|
||||
struct insertion_sort {
|
||||
/**
|
||||
* @brief Sorts the elements in a range.
|
||||
*
|
||||
* Sorts the elements in a range using the given binary comparison function.
|
||||
*
|
||||
* @tparam It Type of random access iterator.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @param first An iterator to the first element of the range to sort.
|
||||
* @param last An iterator past the last element of the range to sort.
|
||||
* @param compare A valid comparison function object.
|
||||
*/
|
||||
template<typename It, typename Compare = std::less<>>
|
||||
void operator()(It first, It last, Compare compare = Compare{}) const {
|
||||
if(first < last) {
|
||||
for(auto it = first + 1; it < last; ++it) {
|
||||
auto value = std::move(*it);
|
||||
auto pre = it;
|
||||
|
||||
for(; pre > first && compare(value, *(pre - 1)); --pre) {
|
||||
*pre = std::move(*(pre - 1));
|
||||
}
|
||||
|
||||
*pre = std::move(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Function object for performing LSD radix sort.
|
||||
* @tparam Bit Number of bits processed per pass.
|
||||
* @tparam N Maximum number of bits to sort.
|
||||
*/
|
||||
template<std::size_t Bit, std::size_t N>
|
||||
struct radix_sort {
|
||||
static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass");
|
||||
|
||||
/**
|
||||
* @brief Sorts the elements in a range.
|
||||
*
|
||||
* Sorts the elements in a range using the given _getter_ to access the
|
||||
* actual data to be sorted.
|
||||
*
|
||||
* This implementation is inspired by the online book
|
||||
* [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort).
|
||||
*
|
||||
* @tparam It Type of random access iterator.
|
||||
* @tparam Getter Type of _getter_ function object.
|
||||
* @param first An iterator to the first element of the range to sort.
|
||||
* @param last An iterator past the last element of the range to sort.
|
||||
* @param getter A valid _getter_ function object.
|
||||
*/
|
||||
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;
|
||||
|
||||
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) {
|
||||
std::size_t index[buckets]{};
|
||||
std::size_t count[buckets]{};
|
||||
|
||||
for(auto it = from; it != to; ++it) {
|
||||
++count[(getter(*it) >> start) & mask];
|
||||
}
|
||||
|
||||
for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) {
|
||||
index[pos + 1u] = index[pos] + count[pos];
|
||||
}
|
||||
|
||||
for(auto it = from; it != to; ++it) {
|
||||
out[index[(getter(*it) >> start) & mask]++] = std::move(*it);
|
||||
}
|
||||
};
|
||||
|
||||
for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) {
|
||||
part(first, last, aux.begin(), pass * Bit);
|
||||
part(aux.begin(), aux.end(), first, (pass + 1) * Bit);
|
||||
}
|
||||
|
||||
if constexpr(passes & 1) {
|
||||
part(first, last, aux.begin(), (passes - 1) * Bit);
|
||||
std::move(aux.begin(), aux.end(), first);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
494
src/entt/core/any.hpp
Normal file
494
src/entt/core/any.hpp
Normal file
@@ -0,0 +1,494 @@
|
||||
#ifndef ENTT_CORE_ANY_HPP
|
||||
#define ENTT_CORE_ANY_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/utility.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "type_info.hpp"
|
||||
#include "type_traits.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief A SBO friendly, type-safe container for single values of any type.
|
||||
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||
* @tparam Align Optional alignment requirement.
|
||||
*/
|
||||
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 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>;
|
||||
|
||||
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;
|
||||
|
||||
if constexpr(in_situ<Type>) {
|
||||
instance = (value.mode == policy::owner) ? ENTT_LAUNDER(reinterpret_cast<const Type *>(&value.storage)) : static_cast<const Type *>(value.instance);
|
||||
} else {
|
||||
instance = 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);
|
||||
}
|
||||
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))};
|
||||
}
|
||||
}
|
||||
|
||||
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)));
|
||||
return other;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case operation::assign:
|
||||
if constexpr(std::is_copy_assignable_v<Type>) {
|
||||
*const_cast<Type *>(instance) = *static_cast<const Type *>(other);
|
||||
return other;
|
||||
}
|
||||
break;
|
||||
case operation::destroy:
|
||||
if constexpr(in_situ<Type>) {
|
||||
instance->~Type();
|
||||
} else if constexpr(std::is_array_v<Type>) {
|
||||
delete[] instance;
|
||||
} else {
|
||||
delete instance;
|
||||
}
|
||||
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;
|
||||
} else {
|
||||
return (instance == other) ? other : nullptr;
|
||||
}
|
||||
case operation::get:
|
||||
return instance;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename Type, typename... Args>
|
||||
void initialize([[maybe_unused]] Args &&...args) {
|
||||
if constexpr(!std::is_void_v<Type>) {
|
||||
vtable = basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>;
|
||||
info = &type_id<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;
|
||||
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 {
|
||||
new(&storage) Type(std::forward<Args>(args)...);
|
||||
}
|
||||
} else {
|
||||
if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
|
||||
instance = new Type{std::forward<Args>(args)...};
|
||||
} else {
|
||||
instance = new Type(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT
|
||||
: instance{other.data()},
|
||||
info{other.info},
|
||||
vtable{other.vtable},
|
||||
mode{pol} {}
|
||||
|
||||
public:
|
||||
/*! @brief Size of the internal storage. */
|
||||
static constexpr auto length = Len;
|
||||
/*! @brief Alignment requirement. */
|
||||
static constexpr auto alignment = Align;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_any() ENTT_NOEXCEPT
|
||||
: instance{},
|
||||
info{&type_id<void>()},
|
||||
vtable{},
|
||||
mode{policy::owner} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a wrapper by directly initializing the new object.
|
||||
* @tparam Type Type of object to use to initialize the wrapper.
|
||||
* @tparam Args Types of arguments to use to construct the new instance.
|
||||
* @param args Parameters to use to construct the instance.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
|
||||
: basic_any{} {
|
||||
initialize<Type>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs a wrapper from a given value.
|
||||
* @tparam Type Type of object to use to initialize the wrapper.
|
||||
* @param value An instance of an object to use to initialize the wrapper.
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
*/
|
||||
basic_any(const basic_any &other)
|
||||
: basic_any{} {
|
||||
if(other.vtable) {
|
||||
other.vtable(operation::copy, other, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_any(basic_any &&other) ENTT_NOEXCEPT
|
||||
: instance{},
|
||||
info{other.info},
|
||||
vtable{other.vtable},
|
||||
mode{other.mode} {
|
||||
if(other.vtable) {
|
||||
other.vtable(operation::move, other, this);
|
||||
}
|
||||
}
|
||||
|
||||
/*! @brief Frees the internal storage, whatever it means. */
|
||||
~basic_any() {
|
||||
if(vtable && mode == policy::owner) {
|
||||
vtable(operation::destroy, *this, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy assignment operator.
|
||||
* @param other The instance to copy from.
|
||||
* @return This any object.
|
||||
*/
|
||||
basic_any &operator=(const basic_any &other) {
|
||||
reset();
|
||||
|
||||
if(other.vtable) {
|
||||
other.vtable(operation::copy, other, this);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This any object.
|
||||
*/
|
||||
basic_any &operator=(basic_any &&other) ENTT_NOEXCEPT {
|
||||
reset();
|
||||
|
||||
if(other.vtable) {
|
||||
other.vtable(operation::move, other, this);
|
||||
info = other.info;
|
||||
vtable = other.vtable;
|
||||
mode = other.mode;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Value assignment operator.
|
||||
* @tparam Type Type of object to use to initialize the wrapper.
|
||||
* @param value An instance of an object to use to initialize the wrapper.
|
||||
* @return This any object.
|
||||
*/
|
||||
template<typename Type>
|
||||
std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>, basic_any &>
|
||||
operator=(Type &&value) {
|
||||
emplace<std::decay_t<Type>>(std::forward<Type>(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the object type if any, `type_id<void>()` otherwise.
|
||||
* @return The object type if any, `type_id<void>()` otherwise.
|
||||
*/
|
||||
[[nodiscard]] const type_info &type() const ENTT_NOEXCEPT {
|
||||
return *info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
return vtable ? vtable(operation::get, *this, nullptr) : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an opaque pointer to the contained instance.
|
||||
* @param req Expected type.
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
[[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT {
|
||||
return *info == req ? data() : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an opaque pointer to the contained instance.
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Replaces the contained object by creating a new instance directly.
|
||||
* @tparam Type Type of object to use to initialize the wrapper.
|
||||
* @tparam Args Types of arguments to use to construct the new instance.
|
||||
* @param args Parameters to use to construct the instance.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
void emplace(Args &&...args) {
|
||||
reset();
|
||||
initialize<Type>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy 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) {
|
||||
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) {
|
||||
if(auto *val = other.data(); val) {
|
||||
return (vtable(operation::transfer, *this, val) != nullptr);
|
||||
} else {
|
||||
return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*! @brief Destroys contained object */
|
||||
void reset() {
|
||||
if(vtable && mode == policy::owner) {
|
||||
vtable(operation::destroy, *this, nullptr);
|
||||
}
|
||||
|
||||
info = &type_id<void>();
|
||||
vtable = nullptr;
|
||||
mode = 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 {
|
||||
return vtable != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if two wrappers differ in their content.
|
||||
* @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 {
|
||||
if(vtable && *info == *other.info) {
|
||||
return (vtable(operation::compare, *this, other.data()) != nullptr);
|
||||
}
|
||||
|
||||
return (!vtable && !other.vtable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)};
|
||||
}
|
||||
|
||||
/*! @copydoc as_ref */
|
||||
[[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT {
|
||||
return basic_any{*this, 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);
|
||||
}
|
||||
|
||||
private:
|
||||
union {
|
||||
const void *instance;
|
||||
storage_type storage;
|
||||
};
|
||||
const type_info *info;
|
||||
vtable_type *vtable;
|
||||
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.
|
||||
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||
* @tparam Align Alignment requirement.
|
||||
* @param data Target any object.
|
||||
* @return The element converted to the requested type.
|
||||
*/
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT {
|
||||
const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
|
||||
ENTT_ASSERT(instance, "Invalid instance");
|
||||
return static_cast<Type>(*instance);
|
||||
}
|
||||
|
||||
/*! @copydoc any_cast */
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
Type any_cast(basic_any<Len, Align> &data) ENTT_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");
|
||||
return static_cast<Type>(*instance);
|
||||
}
|
||||
|
||||
/*! @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>>>) {
|
||||
if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
|
||||
return static_cast<Type>(std::move(*instance));
|
||||
} else {
|
||||
return any_cast<Type>(data);
|
||||
}
|
||||
} else {
|
||||
auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
|
||||
ENTT_ASSERT(instance, "Invalid instance");
|
||||
return static_cast<Type>(std::move(*instance));
|
||||
}
|
||||
}
|
||||
|
||||
/*! @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>>>();
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs a wrapper from a given type, passing it all arguments.
|
||||
* @tparam Type Type of object to use to initialize the wrapper.
|
||||
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||
* @tparam Align Optional alignment requirement.
|
||||
* @tparam Args Types of arguments to use to construct the new instance.
|
||||
* @param args Parameters to use to construct the instance.
|
||||
* @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) {
|
||||
return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forwards its argument and avoids copies for lvalue references.
|
||||
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||
* @tparam Align Optional alignment requirement.
|
||||
* @tparam Type Type of argument to use to construct the new instance.
|
||||
* @param value Parameter to use to construct the instance.
|
||||
* @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)};
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
30
src/entt/core/attribute.h
Normal file
30
src/entt/core/attribute.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef ENTT_CORE_ATTRIBUTE_H
|
||||
#define ENTT_CORE_ATTRIBUTE_H
|
||||
|
||||
#ifndef ENTT_EXPORT
|
||||
# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER
|
||||
# define ENTT_EXPORT __declspec(dllexport)
|
||||
# define ENTT_IMPORT __declspec(dllimport)
|
||||
# define ENTT_HIDDEN
|
||||
# elif defined __GNUC__ && __GNUC__ >= 4
|
||||
# define ENTT_EXPORT __attribute__((visibility("default")))
|
||||
# define ENTT_IMPORT __attribute__((visibility("default")))
|
||||
# define ENTT_HIDDEN __attribute__((visibility("hidden")))
|
||||
# else /* Unsupported compiler */
|
||||
# define ENTT_EXPORT
|
||||
# define ENTT_IMPORT
|
||||
# define ENTT_HIDDEN
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef ENTT_API
|
||||
# if defined ENTT_API_EXPORT
|
||||
# define ENTT_API ENTT_EXPORT
|
||||
# elif defined ENTT_API_IMPORT
|
||||
# define ENTT_API ENTT_IMPORT
|
||||
# else /* No API */
|
||||
# define ENTT_API
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
269
src/entt/core/compressed_pair.hpp
Normal file
269
src/entt/core/compressed_pair.hpp
Normal file
@@ -0,0 +1,269 @@
|
||||
#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP
|
||||
#define ENTT_CORE_COMPRESSED_PAIR_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#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.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Type, std::size_t, typename = void>
|
||||
struct compressed_pair_element {
|
||||
using reference = Type &;
|
||||
using const_reference = const Type &;
|
||||
|
||||
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
|
||||
compressed_pair_element()
|
||||
: 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... Args, std::size_t... Index>
|
||||
compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
|
||||
: value{std::get<Index>(args)...} {}
|
||||
|
||||
[[nodiscard]] reference get() ENTT_NOEXCEPT {
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
Type value;
|
||||
};
|
||||
|
||||
template<typename Type, std::size_t Tag>
|
||||
struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type {
|
||||
using reference = Type &;
|
||||
using const_reference = const Type &;
|
||||
using base_type = Type;
|
||||
|
||||
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
|
||||
compressed_pair_element()
|
||||
: 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... Args, std::size_t... Index>
|
||||
compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
|
||||
: base_type{std::get<Index>(args)...} {}
|
||||
|
||||
[[nodiscard]] reference get() ENTT_NOEXCEPT {
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief A compressed pair.
|
||||
*
|
||||
* A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to
|
||||
* reduce its final size to a minimum.
|
||||
*
|
||||
* @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>
|
||||
class compressed_pair final
|
||||
: internal::compressed_pair_element<First, 0u>,
|
||||
internal::compressed_pair_element<Second, 1u> {
|
||||
using first_base = internal::compressed_pair_element<First, 0u>;
|
||||
using second_base = internal::compressed_pair_element<Second, 1u>;
|
||||
|
||||
public:
|
||||
/*! @brief The type of the first element that the pair stores. */
|
||||
using first_type = First;
|
||||
/*! @brief The type of the second element that the pair stores. */
|
||||
using second_type = Second;
|
||||
|
||||
/**
|
||||
* @brief Default constructor, conditionally enabled.
|
||||
*
|
||||
* This constructor is only available when the types that the pair stores
|
||||
* are both at least default constructible.
|
||||
*
|
||||
* @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()
|
||||
: first_base{},
|
||||
second_base{} {}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
*/
|
||||
constexpr compressed_pair(const compressed_pair &other) = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
constexpr compressed_pair(compressed_pair &&other) ENTT_NOEXCEPT = default;
|
||||
|
||||
/**
|
||||
* @brief Constructs a pair from its values.
|
||||
* @tparam Arg Type of value to use to initialize the first element.
|
||||
* @tparam Other Type of value to use to initialize the second element.
|
||||
* @param arg Value to use to initialize the first element.
|
||||
* @param other Value to use to initialize the second element.
|
||||
*/
|
||||
template<typename Arg, typename Other>
|
||||
constexpr compressed_pair(Arg &&arg, Other &&other)
|
||||
: first_base{std::forward<Arg>(arg)},
|
||||
second_base{std::forward<Other>(other)} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a pair by forwarding the arguments to its parts.
|
||||
* @tparam Args Types of arguments to use to initialize the first element.
|
||||
* @tparam Other Types of arguments to use to initialize the second element.
|
||||
* @param args Arguments to use to initialize the first element.
|
||||
* @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)
|
||||
: first_base{std::move(args), std::index_sequence_for<Args...>{}},
|
||||
second_base{std::move(other), std::index_sequence_for<Other...>{}} {}
|
||||
|
||||
/**
|
||||
* @brief Copy assignment operator.
|
||||
* @param other The instance to copy from.
|
||||
* @return This compressed pair object.
|
||||
*/
|
||||
constexpr compressed_pair &operator=(const compressed_pair &other) = default;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This compressed pair object.
|
||||
*/
|
||||
constexpr compressed_pair &operator=(compressed_pair &&other) ENTT_NOEXCEPT = default;
|
||||
|
||||
/**
|
||||
* @brief Returns the first element that a pair stores.
|
||||
* @return The first element that a pair stores.
|
||||
*/
|
||||
[[nodiscard]] first_type &first() ENTT_NOEXCEPT {
|
||||
return static_cast<first_base &>(*this).get();
|
||||
}
|
||||
|
||||
/*! @copydoc first */
|
||||
[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT {
|
||||
return static_cast<const first_base &>(*this).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the second element that a pair stores.
|
||||
* @return The second element that a pair stores.
|
||||
*/
|
||||
[[nodiscard]] second_type &second() ENTT_NOEXCEPT {
|
||||
return static_cast<second_base &>(*this).get();
|
||||
}
|
||||
|
||||
/*! @copydoc second */
|
||||
[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT {
|
||||
return static_cast<const second_base &>(*this).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swaps two compressed pair objects.
|
||||
* @param other The compressed pair to swap with.
|
||||
*/
|
||||
void swap(compressed_pair &other) {
|
||||
using std::swap;
|
||||
swap(first(), other.first());
|
||||
swap(second(), other.second());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extracts an element from the compressed pair.
|
||||
* @tparam Index An integer value that is either 0 or 1.
|
||||
* @return Returns a reference to the first element if `Index` is 0 and a
|
||||
* reference to the second element if `Index` is 1.
|
||||
*/
|
||||
template<std::size_t Index>
|
||||
decltype(auto) get() ENTT_NOEXCEPT {
|
||||
if constexpr(Index == 0u) {
|
||||
return first();
|
||||
} else {
|
||||
static_assert(Index == 1u, "Index out of bounds");
|
||||
return second();
|
||||
}
|
||||
}
|
||||
|
||||
/*! @copydoc get */
|
||||
template<std::size_t Index>
|
||||
decltype(auto) get() const ENTT_NOEXCEPT {
|
||||
if constexpr(Index == 0u) {
|
||||
return first();
|
||||
} else {
|
||||
static_assert(Index == 1u, "Index out of bounds");
|
||||
return second();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Type Type of value to use to initialize the first element.
|
||||
* @tparam Other Type of value to use to initialize the second element.
|
||||
*/
|
||||
template<typename Type, typename Other>
|
||||
compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>;
|
||||
|
||||
/**
|
||||
* @brief Swaps two compressed pair objects.
|
||||
* @tparam First The type of the first element that the pairs store.
|
||||
* @tparam Second The type of the second element that the pairs store.
|
||||
* @param lhs A valid compressed pair object.
|
||||
* @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) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
// disable structured binding support for clang 6, it messes when specializing tuple_size
|
||||
#if !defined __clang_major__ || __clang_major__ > 6
|
||||
namespace std {
|
||||
|
||||
template<typename First, typename Second>
|
||||
struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {};
|
||||
|
||||
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");
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
#endif
|
||||
|
||||
#endif
|
||||
112
src/entt/core/enum.hpp
Normal file
112
src/entt/core/enum.hpp
Normal file
@@ -0,0 +1,112 @@
|
||||
#ifndef ENTT_CORE_FLAG_HPP
|
||||
#define ENTT_CORE_FLAG_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Enable bitmask support for enum classes.
|
||||
* @tparam Type The enum type for which to enable bitmask support.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
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 {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The enum class type for which to enable bitmask support.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr bool enum_as_bitmask_v = enum_as_bitmask<Type>::value;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
/**
|
||||
* @brief Operator available for enums for which bitmask support is enabled.
|
||||
* @tparam Type Enum class type.
|
||||
* @param lhs The first value to use.
|
||||
* @param rhs The second value to use.
|
||||
* @return The result of invoking the operator on the underlying types of the
|
||||
* 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)};
|
||||
}
|
||||
|
||||
/*! @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)};
|
||||
}
|
||||
|
||||
/*! @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)};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Operator available for enums for which bitmask support is enabled.
|
||||
* @tparam Type Enum class type.
|
||||
* @param value The value to use.
|
||||
* @return The result of invoking the operator on the underlying types of the
|
||||
* 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)};
|
||||
}
|
||||
|
||||
/*! @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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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
|
||||
32
src/entt/core/family.hpp
Normal file
32
src/entt/core/family.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef ENTT_CORE_FAMILY_HPP
|
||||
#define ENTT_CORE_FAMILY_HPP
|
||||
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Dynamic identifier generator.
|
||||
*
|
||||
* Utility class template that can be used to assign unique identifiers to types
|
||||
* at runtime. Use different specializations to create separate sets of
|
||||
* identifiers.
|
||||
*/
|
||||
template<typename...>
|
||||
class family {
|
||||
inline static ENTT_MAYBE_ATOMIC(id_type) identifier{};
|
||||
|
||||
public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using family_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++;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
20
src/entt/core/fwd.hpp
Normal file
20
src/entt/core/fwd.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef ENTT_CORE_FWD_HPP
|
||||
#define ENTT_CORE_FWD_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#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>)>
|
||||
class basic_any;
|
||||
|
||||
/*! @brief Alias declaration for type identifiers. */
|
||||
using id_type = ENTT_ID_TYPE;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using any = basic_any<>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
314
src/entt/core/hashed_string.hpp
Normal file
314
src/entt/core/hashed_string.hpp
Normal file
@@ -0,0 +1,314 @@
|
||||
#ifndef ENTT_CORE_HASHED_STRING_HPP
|
||||
#define ENTT_CORE_HASHED_STRING_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename>
|
||||
struct fnv1a_traits;
|
||||
|
||||
template<>
|
||||
struct fnv1a_traits<std::uint32_t> {
|
||||
using type = std::uint32_t;
|
||||
static constexpr std::uint32_t offset = 2166136261;
|
||||
static constexpr std::uint32_t prime = 16777619;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct fnv1a_traits<std::uint64_t> {
|
||||
using type = std::uint64_t;
|
||||
static constexpr std::uint64_t offset = 14695981039346656037ull;
|
||||
static constexpr std::uint64_t prime = 1099511628211ull;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @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
|
||||
* counterparts at runtime.<br/>
|
||||
* Because of that, a hashed string can also be used in constant expressions if
|
||||
* required.
|
||||
*
|
||||
* @warning
|
||||
* This class doesn't take ownership of user-supplied strings nor does it make a
|
||||
* copy of them.
|
||||
*
|
||||
* @tparam Char Character type.
|
||||
*/
|
||||
template<typename Char>
|
||||
class basic_hashed_string {
|
||||
using hs_traits = 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;
|
||||
};
|
||||
|
||||
// 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;
|
||||
|
||||
while(*curr != 0) {
|
||||
value = (value ^ static_cast<hs_traits::type>(*(curr++))) * hs_traits::prime;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Character type. */
|
||||
using value_type = Char;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using hash_type = id_type;
|
||||
|
||||
/**
|
||||
* @brief Returns directly the numeric representation of a string view.
|
||||
* @param str Human-readable identifer.
|
||||
* @param size 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns directly the numeric representation of a string.
|
||||
* @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);
|
||||
}
|
||||
|
||||
/*! @brief Constructs an empty hashed string. */
|
||||
constexpr basic_hashed_string() ENTT_NOEXCEPT
|
||||
: str{nullptr},
|
||||
hash{} {}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
template<std::size_t N>
|
||||
constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT
|
||||
: str{curr},
|
||||
hash{helper(curr)} {}
|
||||
|
||||
/**
|
||||
* @brief Explicit constructor on purpose to avoid constructing a hashed
|
||||
* string directly from a `const value_type *`.
|
||||
*
|
||||
* @warning
|
||||
* The lifetime of the string is not extended nor is it copied.
|
||||
*
|
||||
* @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)} {}
|
||||
|
||||
/**
|
||||
* @brief Returns the human-readable representation of a hashed string.
|
||||
* @return The string used to initialize the instance.
|
||||
*/
|
||||
[[nodiscard]] constexpr const value_type *data() const ENTT_NOEXCEPT {
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the numeric representation of a hashed string.
|
||||
* @return The numeric representation of the instance.
|
||||
*/
|
||||
[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT {
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*! @copydoc data */
|
||||
[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT {
|
||||
return data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the numeric representation of a hashed string.
|
||||
* @return The numeric representation of the instance.
|
||||
*/
|
||||
[[nodiscard]] constexpr operator hash_type() const ENTT_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.
|
||||
* @tparam N Number of characters of the identifier.
|
||||
* @param str Human-readable identifer.
|
||||
*/
|
||||
template<typename Char, std::size_t N>
|
||||
basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
|
||||
|
||||
/**
|
||||
* @brief Compares two hashed strings.
|
||||
* @tparam Char Character type.
|
||||
* @param lhs A valid hashed string.
|
||||
* @param rhs A valid hashed string.
|
||||
* @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 {
|
||||
return lhs.value() == rhs.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two hashed strings.
|
||||
* @tparam Char Character type.
|
||||
* @param lhs A valid hashed string.
|
||||
* @param rhs A valid hashed string.
|
||||
* @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 {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two hashed strings.
|
||||
* @tparam Char Character type.
|
||||
* @param lhs A valid hashed string.
|
||||
* @param rhs A valid hashed string.
|
||||
* @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 {
|
||||
return lhs.value() < rhs.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two hashed strings.
|
||||
* @tparam Char Character type.
|
||||
* @param lhs A valid hashed string.
|
||||
* @param rhs A valid hashed string.
|
||||
* @return True if the first element is less than or equal to 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 {
|
||||
return !(rhs < lhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two hashed strings.
|
||||
* @tparam Char Character type.
|
||||
* @param lhs A valid hashed string.
|
||||
* @param rhs A valid hashed string.
|
||||
* @return True if the first element is greater 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 {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two hashed strings.
|
||||
* @tparam Char Character type.
|
||||
* @param lhs A valid hashed string.
|
||||
* @param rhs A valid hashed string.
|
||||
* @return True if the first element is greater than or equal to 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 {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
/*! @brief Aliases for common character types. */
|
||||
using hashed_string = basic_hashed_string<char>;
|
||||
|
||||
/*! @brief Aliases for common character types. */
|
||||
using hashed_wstring = basic_hashed_string<wchar_t>;
|
||||
|
||||
inline namespace literals {
|
||||
|
||||
/**
|
||||
* @brief User defined literal for hashed strings.
|
||||
* @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};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief User defined literal for hashed wstrings.
|
||||
* @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};
|
||||
}
|
||||
|
||||
} // namespace literals
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
59
src/entt/core/ident.hpp
Normal file
59
src/entt/core/ident.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef ENTT_CORE_IDENT_HPP
|
||||
#define ENTT_CORE_IDENT_HPP
|
||||
|
||||
#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.
|
||||
*/
|
||||
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{}));
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using identifier_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...>{});
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
116
src/entt/core/iterator.hpp
Normal file
116
src/entt/core/iterator.hpp
Normal file
@@ -0,0 +1,116 @@
|
||||
#ifndef ENTT_CORE_ITERATOR_HPP
|
||||
#define ENTT_CORE_ITERATOR_HPP
|
||||
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Helper type to use as pointer with input iterators.
|
||||
* @tparam Type of wrapped value.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct input_iterator_pointer final {
|
||||
/*! @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;
|
||||
|
||||
/**
|
||||
* @brief Constructs a proxy object by move.
|
||||
* @param val Value to use to initialize the proxy object.
|
||||
*/
|
||||
input_iterator_pointer(Type &&val)
|
||||
: 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 {
|
||||
return std::addressof(value);
|
||||
}
|
||||
|
||||
private:
|
||||
Type value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Utility class to create an iterable object from a pair of iterators.
|
||||
* @tparam It Type of iterators.
|
||||
*/
|
||||
template<typename It>
|
||||
struct iterable_adaptor final {
|
||||
/*! @brief Type of the objects returned during iteration. */
|
||||
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 Default constructor. */
|
||||
iterable_adaptor() = default;
|
||||
|
||||
/**
|
||||
* @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} {}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
* @return An iterator to the first element of the range.
|
||||
*/
|
||||
[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
|
||||
return first;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end.
|
||||
* @return An iterator to the element following the last element of the
|
||||
* range.
|
||||
*/
|
||||
[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
|
||||
return last;
|
||||
}
|
||||
|
||||
/*! @copydoc begin */
|
||||
[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
|
||||
return begin();
|
||||
}
|
||||
|
||||
/*! @copydoc end */
|
||||
[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
|
||||
return end();
|
||||
}
|
||||
|
||||
private:
|
||||
It first;
|
||||
It last;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
108
src/entt/core/memory.hpp
Normal file
108
src/entt/core/memory.hpp
Normal file
@@ -0,0 +1,108 @@
|
||||
#ifndef ENTT_CORE_MEMORY_HPP
|
||||
#define ENTT_CORE_MEMORY_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
|
||||
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.
|
||||
* @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 {
|
||||
return value && ((value & (value - 1)) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Computes the smallest power of two greater than or equal to a value.
|
||||
* @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");
|
||||
std::size_t curr = value - (value != 0u);
|
||||
|
||||
for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
|
||||
curr |= curr >> next;
|
||||
}
|
||||
|
||||
return ++curr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fast module utility function (powers of two only).
|
||||
* @param value A value for which to calculate the modulus.
|
||||
* @param mod _Modulus_, it must be a power of two.
|
||||
* @return The common remainder.
|
||||
*/
|
||||
[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two");
|
||||
return value & (mod - 1u);
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
56
src/entt/core/monostate.hpp
Normal file
56
src/entt/core/monostate.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#ifndef ENTT_CORE_MONOSTATE_HPP
|
||||
#define ENTT_CORE_MONOSTATE_HPP
|
||||
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Minimal implementation of the monostate pattern.
|
||||
*
|
||||
* A minimal, yet complete configuration system built on top of the monostate
|
||||
* pattern. Thread safe by design, it works only with basic types like `int`s or
|
||||
* `bool`s.<br/>
|
||||
* Multiple types and therefore more than one value can be associated with a
|
||||
* single key. Because of this, users must pay attention to use the same type
|
||||
* both during an assignment and when they try to read back their data.
|
||||
* Otherwise, they can incur in unexpected results.
|
||||
*/
|
||||
template<id_type>
|
||||
struct monostate {
|
||||
/**
|
||||
* @brief Assigns a value of a specific type to a given key.
|
||||
* @tparam Type Type of the value to assign.
|
||||
* @param val User data to assign to the given key.
|
||||
*/
|
||||
template<typename Type>
|
||||
void operator=(Type val) const ENTT_NOEXCEPT {
|
||||
value<Type> = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value of a specific type for a given key.
|
||||
* @tparam Type Type of the value to get.
|
||||
* @return Stored value, if any.
|
||||
*/
|
||||
template<typename Type>
|
||||
operator Type() const ENTT_NOEXCEPT {
|
||||
return value<Type>;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename Type>
|
||||
inline static ENTT_MAYBE_ATOMIC(Type) value{};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Value Value used to differentiate between different variables.
|
||||
*/
|
||||
template<id_type Value>
|
||||
inline monostate<Value> monostate_v = {};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
29
src/entt/core/tuple.hpp
Normal file
29
src/entt/core/tuple.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef ENTT_CORE_TUPLE_HPP
|
||||
#define ENTT_CORE_TUPLE_HPP
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Utility function to unwrap tuples of a single element.
|
||||
* @tparam Type Tuple type of any sizes.
|
||||
* @param value A tuple object of the given type.
|
||||
* @return The tuple itself if it contains more than one element, the first
|
||||
* element otherwise.
|
||||
*/
|
||||
template<typename Type>
|
||||
constexpr decltype(auto) unwrap_tuple(Type &&value) ENTT_NOEXCEPT {
|
||||
if constexpr(std::tuple_size_v<std::remove_reference_t<Type>> == 1u) {
|
||||
return std::get<0>(std::forward<Type>(value));
|
||||
} else {
|
||||
return std::forward<Type>(value);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
267
src/entt/core/type_info.hpp
Normal file
267
src/entt/core/type_info.hpp
Normal file
@@ -0,0 +1,267 @@
|
||||
#ifndef ENTT_CORE_TYPE_INFO_HPP
|
||||
#define ENTT_CORE_TYPE_INFO_HPP
|
||||
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/attribute.h"
|
||||
#include "fwd.hpp"
|
||||
#include "hashed_string.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct ENTT_API type_index final {
|
||||
[[nodiscard]] static id_type next() ENTT_NOEXCEPT {
|
||||
static ENTT_MAYBE_ATOMIC(id_type) value{};
|
||||
return value++;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT {
|
||||
#if defined ENTT_PRETTY_FUNCTION
|
||||
std::string_view pretty_function{ENTT_PRETTY_FUNCTION};
|
||||
auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
|
||||
auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
|
||||
return value;
|
||||
#else
|
||||
return std::string_view{""};
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
|
||||
[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT {
|
||||
constexpr auto value = stripped_type_name<Type>();
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] static std::string_view type_name(char) ENTT_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 {
|
||||
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 {
|
||||
static const auto value = [](const auto stripped) {
|
||||
return hashed_string::value(stripped.data(), stripped.size());
|
||||
}(stripped_type_name<Type>());
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Type sequential identifier.
|
||||
* @tparam Type Type for which to generate a sequential identifier.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
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 {
|
||||
static const id_type value = internal::type_index::next();
|
||||
return value;
|
||||
}
|
||||
|
||||
/*! @copydoc value */
|
||||
[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
|
||||
return value();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type hash.
|
||||
* @tparam Type Type for which to generate a hash value.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
struct type_hash final {
|
||||
/**
|
||||
* @brief Returns the numeric representation of a given type.
|
||||
* @return The numeric representation of the given type.
|
||||
*/
|
||||
#if defined ENTT_PRETTY_FUNCTION
|
||||
[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
|
||||
return internal::type_hash<Type>(0);
|
||||
#else
|
||||
[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
|
||||
return type_index<Type>::value();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*! @copydoc value */
|
||||
[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
|
||||
return value();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type name.
|
||||
* @tparam Type Type for which to generate a name.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
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 {
|
||||
return internal::type_name<Type>(0);
|
||||
}
|
||||
|
||||
/*! @copydoc value */
|
||||
[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT {
|
||||
return value();
|
||||
}
|
||||
};
|
||||
|
||||
/*! @brief Implementation specific information about a type. */
|
||||
class type_info final {
|
||||
template<typename Type>
|
||||
friend const type_info &type_id() ENTT_NOEXCEPT;
|
||||
|
||||
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 {
|
||||
return seq;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Type hash.
|
||||
* @return Type hash.
|
||||
*/
|
||||
[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Type name.
|
||||
* @return Type name.
|
||||
*/
|
||||
[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT {
|
||||
return alias;
|
||||
}
|
||||
|
||||
private:
|
||||
id_type seq;
|
||||
id_type identifier;
|
||||
std::string_view alias;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Compares the contents of two type info objects.
|
||||
* @param lhs A type info object.
|
||||
* @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 {
|
||||
return lhs.hash() == rhs.hash();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares the contents of two type info objects.
|
||||
* @param lhs A type info object.
|
||||
* @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 {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two type info objects.
|
||||
* @param lhs A valid type info object.
|
||||
* @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 {
|
||||
return lhs.index() < rhs.index();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two type info objects.
|
||||
* @param lhs A valid type info object.
|
||||
* @param rhs A valid type info object.
|
||||
* @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 {
|
||||
return !(rhs < lhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two type info objects.
|
||||
* @param lhs A valid type info object.
|
||||
* @param rhs A valid type info object.
|
||||
* @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 {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two type info objects.
|
||||
* @param lhs A valid type info object.
|
||||
* @param rhs A valid type info object.
|
||||
* @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 {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the type info object associated to a given type.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] const type_info &type_id() ENTT_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>>>};
|
||||
return instance;
|
||||
} else {
|
||||
return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
678
src/entt/core/type_traits.hpp
Normal file
678
src/entt/core/type_traits.hpp
Normal file
@@ -0,0 +1,678 @@
|
||||
#ifndef ENTT_CORE_TYPE_TRAITS_HPP
|
||||
#define ENTT_CORE_TYPE_TRAITS_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Utility class to disambiguate overloaded functions.
|
||||
* @tparam N Number of choices available.
|
||||
*/
|
||||
template<std::size_t N>
|
||||
struct choice_t
|
||||
// Unfortunately, doxygen cannot parse such a construct.
|
||||
: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
|
||||
{};
|
||||
|
||||
/*! @copybrief choice_t */
|
||||
template<>
|
||||
struct choice_t<0> {};
|
||||
|
||||
/**
|
||||
* @brief Variable template for the choice trick.
|
||||
* @tparam N Number of choices available.
|
||||
*/
|
||||
template<std::size_t N>
|
||||
inline constexpr choice_t<N> choice{};
|
||||
|
||||
/**
|
||||
* @brief Identity type trait.
|
||||
*
|
||||
* Useful to establish non-deduced contexts in template argument deduction
|
||||
* (waiting for C++20) or to provide types through function arguments.
|
||||
*
|
||||
* @tparam Type A type.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct type_identity {
|
||||
/*! @brief Identity type. */
|
||||
using type = Type;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Type A type.
|
||||
*/
|
||||
template<typename Type>
|
||||
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> {};
|
||||
|
||||
/*! @copydoc size_of */
|
||||
template<typename Type>
|
||||
struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
|
||||
: std::integral_constant<std::size_t, sizeof(Type)> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The type of which to return the size.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr std::size_t size_of_v = size_of<Type>::value;
|
||||
|
||||
/**
|
||||
* @brief Using declaration to be used to _repeat_ the same type a number of
|
||||
* times equal to the size of a given parameter pack.
|
||||
* @tparam Type A type to repeat.
|
||||
*/
|
||||
template<typename Type, typename>
|
||||
using unpack_as_type = Type;
|
||||
|
||||
/**
|
||||
* @brief Helper variable template to be used to _repeat_ the same value a
|
||||
* number of times equal to the size of a given parameter pack.
|
||||
* @tparam Value A value to repeat.
|
||||
*/
|
||||
template<auto Value, typename>
|
||||
inline constexpr auto unpack_as_value = Value;
|
||||
|
||||
/**
|
||||
* @brief Wraps a static constant.
|
||||
* @tparam Value A static constant.
|
||||
*/
|
||||
template<auto Value>
|
||||
using integral_constant = std::integral_constant<decltype(Value), Value>;
|
||||
|
||||
/**
|
||||
* @brief Alias template to facilitate the creation of named values.
|
||||
* @tparam Value A constant value at least convertible to `id_type`.
|
||||
*/
|
||||
template<id_type Value>
|
||||
using tag = integral_constant<Value>;
|
||||
|
||||
/**
|
||||
* @brief A class to use to push around lists of types, nothing more.
|
||||
* @tparam Type Types provided by the type list.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct type_list {
|
||||
/*! @brief Type list type. */
|
||||
using type = type_list;
|
||||
/*! @brief Compile-time number of elements in the type list. */
|
||||
static constexpr auto size = sizeof...(Type);
|
||||
};
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<std::size_t, typename>
|
||||
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 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...>>
|
||||
: 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 Other Other types provided by the type list.
|
||||
*/
|
||||
template<typename Type, typename... Other>
|
||||
struct type_list_element<0u, type_list<Type, Other...>> {
|
||||
/*! @brief Searched type. */
|
||||
using type = Type;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Index Index of the type to return.
|
||||
* @tparam List Type list to search into.
|
||||
*/
|
||||
template<std::size_t Index, typename List>
|
||||
using type_list_element_t = typename type_list_element<Index, List>::type;
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple type lists.
|
||||
* @tparam Type Types provided by the first type list.
|
||||
* @tparam Other Types provided by the second type list.
|
||||
* @return A type list composed by the types of both the type lists.
|
||||
*/
|
||||
template<typename... Type, typename... Other>
|
||||
constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename...>
|
||||
struct type_list_cat;
|
||||
|
||||
/*! @brief Concatenates multiple type lists. */
|
||||
template<>
|
||||
struct type_list_cat<> {
|
||||
/*! @brief A type list composed by the types of all the type lists. */
|
||||
using type = type_list<>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple type lists.
|
||||
* @tparam Type Types provided by the first type list.
|
||||
* @tparam Other Types provided by the second type list.
|
||||
* @tparam List Other type lists, if any.
|
||||
*/
|
||||
template<typename... Type, typename... Other, typename... List>
|
||||
struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
|
||||
/*! @brief A type list composed by the types of all the type lists. */
|
||||
using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple type lists.
|
||||
* @tparam Type Types provided by the type list.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct type_list_cat<type_list<Type...>> {
|
||||
/*! @brief A type list composed by the types of all the type lists. */
|
||||
using type = type_list<Type...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam List Type lists to concatenate.
|
||||
*/
|
||||
template<typename... List>
|
||||
using type_list_cat_t = typename type_list_cat<List...>::type;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename>
|
||||
struct type_list_unique;
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
template<typename Type, typename... Other>
|
||||
struct type_list_unique<type_list<Type, Other...>> {
|
||||
/*! @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<>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Type A type list.
|
||||
*/
|
||||
template<typename Type>
|
||||
using type_list_unique_t = typename type_list_unique<Type>::type;
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a type list contains a
|
||||
* given type, false otherwise.
|
||||
* @tparam List Type list.
|
||||
* @tparam Type Type to look for.
|
||||
*/
|
||||
template<typename List, typename Type>
|
||||
struct type_list_contains;
|
||||
|
||||
/**
|
||||
* @copybrief type_list_contains
|
||||
* @tparam Type Types provided by the type list.
|
||||
* @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>...> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam List Type list.
|
||||
* @tparam Type Type to look for.
|
||||
*/
|
||||
template<typename List, typename Type>
|
||||
inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename...>
|
||||
struct type_list_diff;
|
||||
|
||||
/**
|
||||
* @brief Computes the difference between two type lists.
|
||||
* @tparam Type Types provided by the first type list.
|
||||
* @tparam Other Types provided by the second type list.
|
||||
*/
|
||||
template<typename... Type, typename... Other>
|
||||
struct type_list_diff<type_list<Type...>, type_list<Other...>> {
|
||||
/*! @brief A type list that is the difference between the two type lists. */
|
||||
using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam List Type lists between which to compute the difference.
|
||||
*/
|
||||
template<typename... List>
|
||||
using type_list_diff_t = typename type_list_diff<List...>::type;
|
||||
|
||||
/**
|
||||
* @brief A class to use to push around lists of constant values, nothing more.
|
||||
* @tparam Value Values provided by the value list.
|
||||
*/
|
||||
template<auto... Value>
|
||||
struct value_list {
|
||||
/*! @brief Value list type. */
|
||||
using type = value_list;
|
||||
/*! @brief Compile-time number of elements in the value list. */
|
||||
static constexpr auto size = sizeof...(Value);
|
||||
};
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<std::size_t, typename>
|
||||
struct value_list_element;
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time indexed access to the values of a value list.
|
||||
* @tparam Index Index of the value to return.
|
||||
* @tparam Value First value provided by the value list.
|
||||
* @tparam Other Other values provided by the value list.
|
||||
*/
|
||||
template<std::size_t Index, auto Value, auto... Other>
|
||||
struct value_list_element<Index, value_list<Value, Other...>>
|
||||
: value_list_element<Index - 1u, value_list<Other...>> {};
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time indexed access to the types of a type list.
|
||||
* @tparam Value First value provided by the value list.
|
||||
* @tparam Other Other values provided by the value list.
|
||||
*/
|
||||
template<auto Value, auto... Other>
|
||||
struct value_list_element<0u, value_list<Value, Other...>> {
|
||||
/*! @brief Searched value. */
|
||||
static constexpr auto value = Value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Index Index of the value to return.
|
||||
* @tparam List Value list to search into.
|
||||
*/
|
||||
template<std::size_t Index, typename List>
|
||||
inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple value lists.
|
||||
* @tparam Value Values provided by the first value list.
|
||||
* @tparam Other Values provided by the second value list.
|
||||
* @return A value list composed by the values of both the value lists.
|
||||
*/
|
||||
template<auto... Value, auto... Other>
|
||||
constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename...>
|
||||
struct value_list_cat;
|
||||
|
||||
/*! @brief Concatenates multiple value lists. */
|
||||
template<>
|
||||
struct value_list_cat<> {
|
||||
/*! @brief A value list composed by the values of all the value lists. */
|
||||
using type = value_list<>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple value lists.
|
||||
* @tparam Value Values provided by the first value list.
|
||||
* @tparam Other Values provided by the second value list.
|
||||
* @tparam List Other value lists, if any.
|
||||
*/
|
||||
template<auto... Value, auto... Other, typename... List>
|
||||
struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
|
||||
/*! @brief A value list composed by the values of all the value lists. */
|
||||
using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple value lists.
|
||||
* @tparam Value Values provided by the value list.
|
||||
*/
|
||||
template<auto... Value>
|
||||
struct value_list_cat<value_list<Value...>> {
|
||||
/*! @brief A value list composed by the values of all the value lists. */
|
||||
using type = value_list<Value...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam List Value lists to concatenate.
|
||||
*/
|
||||
template<typename... List>
|
||||
using value_list_cat_t = typename value_list_cat<List...>::type;
|
||||
|
||||
/*! @brief Same as std::is_invocable, but with tuples. */
|
||||
template<typename, typename>
|
||||
struct is_applicable: std::false_type {};
|
||||
|
||||
/**
|
||||
* @copybrief is_applicable
|
||||
* @tparam Func A valid function type.
|
||||
* @tparam Tuple Tuple-like type.
|
||||
* @tparam Args The list of arguments to use to probe the function type.
|
||||
*/
|
||||
template<typename Func, template<typename...> class Tuple, typename... Args>
|
||||
struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
|
||||
|
||||
/**
|
||||
* @copybrief is_applicable
|
||||
* @tparam Func A valid function type.
|
||||
* @tparam Tuple Tuple-like type.
|
||||
* @tparam Args The list of arguments to use to probe the function type.
|
||||
*/
|
||||
template<typename Func, template<typename...> class Tuple, typename... Args>
|
||||
struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Func A valid function type.
|
||||
* @tparam Args The list of arguments to use to probe the function type.
|
||||
*/
|
||||
template<typename Func, typename Args>
|
||||
inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
|
||||
|
||||
/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
|
||||
template<typename, typename, typename>
|
||||
struct is_applicable_r: std::false_type {};
|
||||
|
||||
/**
|
||||
* @copybrief is_applicable_r
|
||||
* @tparam Ret The type to which the return type of the function should be
|
||||
* convertible.
|
||||
* @tparam Func A valid function type.
|
||||
* @tparam Args The list of arguments to use to probe the function type.
|
||||
*/
|
||||
template<typename Ret, typename Func, typename... Args>
|
||||
struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Ret The type to which the return type of the function should be
|
||||
* convertible.
|
||||
* @tparam Func A valid function type.
|
||||
* @tparam Args The list of arguments to use to probe the function type.
|
||||
*/
|
||||
template<typename Ret, typename Func, typename Args>
|
||||
inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type is
|
||||
* complete, false otherwise.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
struct is_complete: std::false_type {};
|
||||
|
||||
/*! @copydoc is_complete */
|
||||
template<typename Type>
|
||||
struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr bool is_complete_v = is_complete<Type>::value;
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type is an
|
||||
* iterator, false otherwise.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
struct is_iterator: std::false_type {};
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename, typename = void>
|
||||
struct has_iterator_category: std::false_type {};
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
/*! @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>>>
|
||||
: internal::has_iterator_category<Type> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
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.
|
||||
* @tparam Type The type to test
|
||||
*/
|
||||
template<typename Type>
|
||||
struct is_ebco_eligible
|
||||
: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if `Type::is_transparent`
|
||||
* is valid and denotes a type, false otherwise.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
struct is_transparent: std::false_type {};
|
||||
|
||||
/*! @copydoc is_transparent */
|
||||
template<typename Type>
|
||||
struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename, typename = void>
|
||||
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 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 && ...);
|
||||
}
|
||||
|
||||
template<typename>
|
||||
[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
|
||||
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>);
|
||||
} 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>);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/*! @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>)> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
|
||||
|
||||
/**
|
||||
* @brief Transcribes the constness of a type to another type.
|
||||
* @tparam To The type to which to transcribe the constness.
|
||||
* @tparam From The type from which to transcribe the constness.
|
||||
*/
|
||||
template<typename To, typename From>
|
||||
struct constness_as {
|
||||
/*! @brief The type resulting from the transcription of the constness. */
|
||||
using type = std::remove_const_t<To>;
|
||||
};
|
||||
|
||||
/*! @copydoc 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>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Alias template to facilitate the transcription of the constness.
|
||||
* @tparam To The type to which to transcribe the constness.
|
||||
* @tparam From The type from which to transcribe the constness.
|
||||
*/
|
||||
template<typename To, typename From>
|
||||
using constness_as_t = typename constness_as<To, From>::type;
|
||||
|
||||
/**
|
||||
* @brief Extracts the class of a non-static member object or function.
|
||||
* @tparam Member A pointer to a non-static member object or function.
|
||||
*/
|
||||
template<typename Member>
|
||||
class member_class {
|
||||
static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
|
||||
|
||||
template<typename Class, typename Ret, typename... Args>
|
||||
static Class *clazz(Ret (Class::*)(Args...));
|
||||
|
||||
template<typename Class, typename Ret, typename... Args>
|
||||
static Class *clazz(Ret (Class::*)(Args...) const);
|
||||
|
||||
template<typename Class, typename Type>
|
||||
static Class *clazz(Type Class::*);
|
||||
|
||||
public:
|
||||
/*! @brief The class of the given non-static member object or function. */
|
||||
using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Member A pointer to a non-static member object or function.
|
||||
*/
|
||||
template<typename Member>
|
||||
using member_class_t = typename member_class<Member>::type;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
101
src/entt/core/utility.hpp
Normal file
101
src/entt/core/utility.hpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#ifndef ENTT_CORE_UTILITY_HPP
|
||||
#define ENTT_CORE_UTILITY_HPP
|
||||
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @brief Identity function object (waiting for C++20). */
|
||||
struct identity {
|
||||
/*! @brief Indicates that this is a transparent function object. */
|
||||
using is_transparent = void;
|
||||
|
||||
/**
|
||||
* @brief Returns its argument unchanged.
|
||||
* @tparam Type Type of the argument.
|
||||
* @param value The actual argument.
|
||||
* @return The submitted value as-is.
|
||||
*/
|
||||
template<class Type>
|
||||
[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT {
|
||||
return std::forward<Type>(value);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Constant utility to disambiguate overloaded members of a class.
|
||||
* @tparam Type Type of the desired overload.
|
||||
* @tparam Class Type of class to which the member belongs.
|
||||
* @param member A valid pointer to a member.
|
||||
* @return Pointer to the member.
|
||||
*/
|
||||
template<typename Type, typename Class>
|
||||
[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT {
|
||||
return member;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constant utility to disambiguate overloaded functions.
|
||||
* @tparam Func Function type of the desired overload.
|
||||
* @param func A valid pointer to a function.
|
||||
* @return Pointer to the function.
|
||||
*/
|
||||
template<typename Func>
|
||||
[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT {
|
||||
return func;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper type for visitors.
|
||||
* @tparam Func Types of function objects.
|
||||
*/
|
||||
template<class... Func>
|
||||
struct overloaded: Func... {
|
||||
using Func::operator()...;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Func Types of function objects.
|
||||
*/
|
||||
template<class... Func>
|
||||
overloaded(Func...) -> overloaded<Func...>;
|
||||
|
||||
/**
|
||||
* @brief Basic implementation of a y-combinator.
|
||||
* @tparam Func Type of a potentially recursive function.
|
||||
*/
|
||||
template<class Func>
|
||||
struct y_combinator {
|
||||
/**
|
||||
* @brief Constructs a y-combinator from a given function.
|
||||
* @param recursive A potentially recursive function.
|
||||
*/
|
||||
y_combinator(Func recursive)
|
||||
: func{std::move(recursive)} {}
|
||||
|
||||
/**
|
||||
* @brief Invokes a y-combinator and therefore its underlying function.
|
||||
* @tparam Args Types of arguments to use to invoke the underlying function.
|
||||
* @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 {
|
||||
return func(*this, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/*! @copydoc operator()() */
|
||||
template<class... Args>
|
||||
decltype(auto) operator()(Args &&...args) {
|
||||
return func(*this, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
Func func;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
37
src/entt/entity/component.hpp
Normal file
37
src/entt/entity/component.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef ENTT_ENTITY_COMPONENT_HPP
|
||||
#define ENTT_ENTITY_COMPONENT_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
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>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
339
src/entt/entity/entity.hpp
Normal file
339
src/entt/entity/entity.hpp
Normal file
@@ -0,0 +1,339 @@
|
||||
#ifndef ENTT_ENTITY_ENTITY_HPP
|
||||
#define ENTT_ENTITY_ENTITY_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
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>> {};
|
||||
|
||||
template<typename Type>
|
||||
struct entt_traits<Type, std::enable_if_t<std::is_class_v<Type>>>
|
||||
: entt_traits<typename Type::entity_type> {};
|
||||
|
||||
template<>
|
||||
struct entt_traits<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 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
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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>;
|
||||
|
||||
public:
|
||||
/*! @brief Value type. */
|
||||
using value_type = Type;
|
||||
/*! @brief Underlying entity type. */
|
||||
using entity_type = typename base_type::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;
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
return static_cast<entity_type>(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the entity part once converted to the underlying type.
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the version part once converted to the underlying type.
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs an identifier from its parts.
|
||||
*
|
||||
* If the version part is not provided, a tombstone is returned.<br/>
|
||||
* If the entity part is not provided, a null identifier is returned.
|
||||
*
|
||||
* @param entity The entity part of the identifier.
|
||||
* @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)};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Combines two identifiers in a single one.
|
||||
*
|
||||
* The returned identifier is a copy of the first element except for its
|
||||
* version, which is taken from the second element.
|
||||
*
|
||||
* @param lhs The identifier from which to take the entity part.
|
||||
* @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)};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @copydoc entt_traits<Entity>::to_integral
|
||||
* @tparam Entity The value type.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_integral(const Entity value) ENTT_NOEXCEPT {
|
||||
return entt_traits<Entity>::to_integral(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc entt_traits<Entity>::to_entity
|
||||
* @tparam Entity The value type.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_entity(const Entity value) ENTT_NOEXCEPT {
|
||||
return entt_traits<Entity>::to_entity(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc entt_traits<Entity>::to_version
|
||||
* @tparam Entity The value type.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr typename entt_traits<Entity>::version_type to_version(const Entity value) ENTT_NOEXCEPT {
|
||||
return entt_traits<Entity>::to_version(value);
|
||||
}
|
||||
|
||||
/*! @brief Null object for all identifiers. */
|
||||
struct null_t {
|
||||
/**
|
||||
* @brief Converts the null object to identifiers of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two null objects.
|
||||
* @param other A null object.
|
||||
* @return True in all cases.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two null objects.
|
||||
* @param other A null object.
|
||||
* @return False in all cases.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a null object and an identifier of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @param entity Identifier with which to compare.
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a null object and an identifier of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @param entity Identifier with which to compare.
|
||||
* @return True if the two elements differ, false otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
|
||||
return !(entity == *this);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Compares a null object and an identifier of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @param entity Identifier with which to compare.
|
||||
* @param other A null object yet to be converted.
|
||||
* @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 {
|
||||
return other.operator==(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a null object and an identifier of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @param entity Identifier with which to compare.
|
||||
* @param other A null object yet to be converted.
|
||||
* @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 {
|
||||
return !(other == entity);
|
||||
}
|
||||
|
||||
/*! @brief Tombstone object for all identifiers. */
|
||||
struct tombstone_t {
|
||||
/**
|
||||
* @brief Converts the tombstone object to identifiers of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two tombstone objects.
|
||||
* @param other A tombstone object.
|
||||
* @return True in all cases.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two tombstone objects.
|
||||
* @param other A tombstone object.
|
||||
* @return False in all cases.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a tombstone object and an identifier of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @param entity Identifier with which to compare.
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a tombstone object and an identifier of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @param entity Identifier with which to compare.
|
||||
* @return True if the two elements differ, false otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
|
||||
return !(entity == *this);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Compares a tombstone object and an identifier of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @param entity Identifier with which to compare.
|
||||
* @param other A tombstone object yet to be converted.
|
||||
* @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 {
|
||||
return other.operator==(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a tombstone object and an identifier of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @param entity Identifier with which to compare.
|
||||
* @param other A tombstone object yet to be converted.
|
||||
* @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 {
|
||||
return !(other == 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
|
||||
* entity and any other identifier.
|
||||
*/
|
||||
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
|
||||
* tombstone entity and any other identifier.
|
||||
*/
|
||||
inline constexpr tombstone_t tombstone{};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
117
src/entt/entity/fwd.hpp
Normal file
117
src/entt/entity/fwd.hpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#ifndef ENTT_ENTITY_FWD_HPP
|
||||
#define ENTT_ENTITY_FWD_HPP
|
||||
|
||||
#include <memory>
|
||||
#include "../core/fwd.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
template<typename Entity, typename = std::allocator<Entity>>
|
||||
class basic_sparse_set;
|
||||
|
||||
template<typename, typename Type, typename = std::allocator<Type>, typename = void>
|
||||
class basic_storage;
|
||||
|
||||
template<typename>
|
||||
class basic_registry;
|
||||
|
||||
template<typename, typename, typename, typename = void>
|
||||
class basic_view;
|
||||
|
||||
template<typename>
|
||||
class basic_runtime_view;
|
||||
|
||||
template<typename, typename, typename, typename>
|
||||
class basic_group;
|
||||
|
||||
template<typename>
|
||||
class basic_observer;
|
||||
|
||||
template<typename>
|
||||
class basic_organizer;
|
||||
|
||||
template<typename, typename...>
|
||||
struct basic_handle;
|
||||
|
||||
template<typename>
|
||||
class basic_snapshot;
|
||||
|
||||
template<typename>
|
||||
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<entity>;
|
||||
|
||||
/**
|
||||
* @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>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using handle_view = basic_handle<entity, Args...>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using const_handle_view = basic_handle<const entity, Args...>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using snapshot = basic_snapshot<entity>;
|
||||
|
||||
/*! @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 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.
|
||||
*/
|
||||
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>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using group = basic_group<entity, Args...>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
904
src/entt/entity/group.hpp
Normal file
904
src/entt/entity/group.hpp
Normal file
@@ -0,0 +1,904 @@
|
||||
#ifndef ENTT_ENTITY_GROUP_HPP
|
||||
#define ENTT_ENTITY_GROUP_HPP
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/iterator.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "component.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "sparse_set.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Group.
|
||||
*
|
||||
* Primary template isn't defined on purpose. All the specializations give a
|
||||
* compile-time error, but for a few reasonable cases.
|
||||
*/
|
||||
template<typename, typename, typename, typename>
|
||||
class basic_group;
|
||||
|
||||
/**
|
||||
* @brief Non-owning group.
|
||||
*
|
||||
* A non-owning group returns all entities and only the entities that have at
|
||||
* least the given components. Moreover, it's guaranteed that the entity list
|
||||
* is tightly packed in memory for fast iterations.
|
||||
*
|
||||
* @b Important
|
||||
*
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all other cases, modifying the pools iterated by the group in any way
|
||||
* invalidates all the iterators and using them results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Groups 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 all the
|
||||
* groups.<br/>
|
||||
* Moreover, sorting a non-owning group affects all the instances of the same
|
||||
* group (it means that users don't have to call `sort` on each instance to sort
|
||||
* all of them because they _share_ entities and components).
|
||||
*
|
||||
* @warning
|
||||
* Lifetime of a group must not overcome that of the registry that generated it.
|
||||
* In any other case, attempting to use a group results in undefined behavior.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Get Type of components observed by the group.
|
||||
* @tparam Exclude Types of components used to filter the group.
|
||||
*/
|
||||
template<typename Entity, typename... Get, typename... Exclude>
|
||||
class basic_group<Entity, owned_t<>, get_t<Get...>, exclude_t<Exclude...>> final {
|
||||
/*! @brief A registry is allowed to create groups. */
|
||||
friend class basic_registry<Entity>;
|
||||
|
||||
template<typename Comp>
|
||||
using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>;
|
||||
|
||||
using basic_common_type = std::common_type_t<typename storage_type<Get>::base_type...>;
|
||||
|
||||
struct extended_group_iterator final {
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
extended_group_iterator() = default;
|
||||
|
||||
extended_group_iterator(typename basic_common_type::iterator from, const std::tuple<storage_type<Get> *...> &args) ENTT_NOEXCEPT
|
||||
: it{from},
|
||||
pools{args} {}
|
||||
|
||||
extended_group_iterator &operator++() ENTT_NOEXCEPT {
|
||||
return ++it, *this;
|
||||
}
|
||||
|
||||
extended_group_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
extended_group_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
|
||||
const auto entt = *it;
|
||||
return std::tuple_cat(std::make_tuple(entt), std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
|
||||
}
|
||||
|
||||
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
|
||||
return operator*();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.it == it;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
typename basic_common_type::iterator it;
|
||||
std::tuple<storage_type<Get> *...> pools;
|
||||
};
|
||||
|
||||
basic_group(basic_common_type &ref, storage_type<Get> &...gpool) ENTT_NOEXCEPT
|
||||
: handler{&ref},
|
||||
pools{&gpool...} {}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Common type among all storage types. */
|
||||
using base_type = basic_common_type;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = typename base_type::iterator;
|
||||
/*! @brief Reversed iterator type. */
|
||||
using reverse_iterator = typename base_type::reverse_iterator;
|
||||
/*! @brief Iterable group type. */
|
||||
using iterable = iterable_adaptor<extended_group_iterator>;
|
||||
|
||||
/*! @brief Default constructor to use to create empty, invalid groups. */
|
||||
basic_group() ENTT_NOEXCEPT
|
||||
: handler{} {}
|
||||
|
||||
/**
|
||||
* @brief Returns a const reference to the underlying handler.
|
||||
* @return A const reference to the underlying handler.
|
||||
*/
|
||||
const base_type &handle() const ENTT_NOEXCEPT {
|
||||
return *handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the storage for a given component type.
|
||||
* @tparam Comp Type of component of which to return the storage.
|
||||
* @return The storage for the given component type.
|
||||
*/
|
||||
template<typename Comp>
|
||||
[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
|
||||
return *std::get<storage_type<Comp> *>(pools);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the storage for a given component type.
|
||||
* @tparam Comp Index of component of which to return the storage.
|
||||
* @return The storage for the given component type.
|
||||
*/
|
||||
template<std::size_t Comp>
|
||||
[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
|
||||
return *std::get<Comp>(pools);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of entities that have the given components.
|
||||
* @return Number of entities that have the given components.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
|
||||
return *this ? handler->size() : size_type{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements that a group has currently
|
||||
* allocated space for.
|
||||
* @return Capacity of the group.
|
||||
*/
|
||||
[[nodiscard]] size_type capacity() const ENTT_NOEXCEPT {
|
||||
return *this ? handler->capacity() : size_type{};
|
||||
}
|
||||
|
||||
/*! @brief Requests the removal of unused capacity. */
|
||||
void shrink_to_fit() {
|
||||
if(*this) {
|
||||
handler->shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a group is empty.
|
||||
* @return True if the group is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return !*this || handler->empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the group.
|
||||
*
|
||||
* The returned iterator points to the first entity of the group. If the
|
||||
* group is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the group.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
|
||||
return *this ? handler->begin() : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the group.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the group. Attempting to dereference the returned iterator results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* group.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
|
||||
return *this ? handler->end() : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the reversed group.
|
||||
*
|
||||
* The returned iterator points to the first entity of the reversed group.
|
||||
* If the group is empty, the returned iterator will be equal to `rend()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the reversed group.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
|
||||
return *this ? handler->rbegin() : reverse_iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the reversed
|
||||
* group.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the reversed group. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* reversed group.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
|
||||
return *this ? handler->rend() : reverse_iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the first entity of the group, if any.
|
||||
* @return The first entity of the group if one exists, the null entity
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] entity_type front() const {
|
||||
const auto it = begin();
|
||||
return it != end() ? *it : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the last entity of the group, if any.
|
||||
* @return The last entity of the group if one exists, the null entity
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] entity_type back() const {
|
||||
const auto it = rbegin();
|
||||
return it != rend() ? *it : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds an entity.
|
||||
* @param entt A valid identifier.
|
||||
* @return An iterator to the given entity if it's found, past the end
|
||||
* iterator otherwise.
|
||||
*/
|
||||
[[nodiscard]] iterator find(const entity_type entt) const {
|
||||
const auto it = *this ? handler->find(entt) : iterator{};
|
||||
return it != end() && *it == entt ? it : end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the identifier that occupies the given position.
|
||||
* @param pos Position of the element to return.
|
||||
* @return The identifier that occupies the given position.
|
||||
*/
|
||||
[[nodiscard]] entity_type operator[](const size_type pos) const {
|
||||
return begin()[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a group is properly initialized.
|
||||
* @return True if the group is properly initialized, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return handler != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a group contains an entity.
|
||||
* @param entt A valid identifier.
|
||||
* @return True if the group contains the given entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(const entity_type entt) const {
|
||||
return *this && handler->contains(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
*
|
||||
* Prefer this function instead of `registry::get` during iterations. It has
|
||||
* far better performance than its counterpart.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an invalid component type results in a compilation
|
||||
* error. Attempting to use an entity that doesn't belong to the group
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @tparam Comp Types of components to get.
|
||||
* @param entt A valid identifier.
|
||||
* @return The components assigned to the entity.
|
||||
*/
|
||||
template<typename... Comp>
|
||||
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
|
||||
ENTT_ASSERT(contains(entt), "Group does not contain entity");
|
||||
|
||||
if constexpr(sizeof...(Comp) == 0) {
|
||||
return std::tuple_cat(std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
|
||||
} else if constexpr(sizeof...(Comp) == 1) {
|
||||
return (std::get<storage_type<Comp> *>(pools)->get(entt), ...);
|
||||
} else {
|
||||
return std::tuple_cat(std::get<storage_type<Comp> *>(pools)->get_as_tuple(entt)...);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and components and applies the given function
|
||||
* object to them.
|
||||
*
|
||||
* The function object is invoked for each entity. It is provided with the
|
||||
* entity itself and a set of references to non-empty components. The
|
||||
* _constness_ of the components is as requested.<br/>
|
||||
* The signature of the function must be equivalent to one of the following
|
||||
* forms:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type, Type &...);
|
||||
* void(Type &...);
|
||||
* @endcode
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated and therefore they are never
|
||||
* returned during iterations.
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
for(const auto entt: *this) {
|
||||
if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) {
|
||||
std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt)));
|
||||
} else {
|
||||
std::apply(func, get(entt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to use to _visit_ a group.
|
||||
*
|
||||
* The iterable object returns tuples that contain the current entity and a
|
||||
* set of references to its non-empty components. The _constness_ of the
|
||||
* components is as requested.
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated and therefore they are never
|
||||
* returned during iterations.
|
||||
*
|
||||
* @return An iterable object to use to _visit_ the group.
|
||||
*/
|
||||
[[nodiscard]] iterable each() const ENTT_NOEXCEPT {
|
||||
return handler ? iterable{extended_group_iterator{handler->begin(), pools}, extended_group_iterator{handler->end(), pools}}
|
||||
: iterable{extended_group_iterator{{}, pools}, extended_group_iterator{{}, pools}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort a group according to the given comparison function.
|
||||
*
|
||||
* Sort the group so that iterating it with a couple of iterators returns
|
||||
* entities and components in the expected order. See `begin` and `end` for
|
||||
* more details.
|
||||
*
|
||||
* The comparison function object must return `true` if the first element
|
||||
* is _less_ than the second one, `false` otherwise. The signature of the
|
||||
* comparison function should be equivalent to one of the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* bool(std::tuple<Component &...>, std::tuple<Component &...>);
|
||||
* bool(const Component &..., const Component &...);
|
||||
* bool(const Entity, const Entity);
|
||||
* @endcode
|
||||
*
|
||||
* Where `Component` are such that they are iterated by the group.<br/>
|
||||
* Moreover, the comparison function object shall induce a
|
||||
* _strict weak ordering_ on the values.
|
||||
*
|
||||
* The sort function object must offer a member function template
|
||||
* `operator()` that accepts three arguments:
|
||||
*
|
||||
* * An iterator to the first element of the range to sort.
|
||||
* * An iterator past the last element of the range to sort.
|
||||
* * A comparison function to use to compare the elements.
|
||||
*
|
||||
* @tparam Comp Optional types of components to compare.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Sort Type of sort function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function object.
|
||||
* @param compare A valid comparison function object.
|
||||
* @param algo A valid sort function object.
|
||||
* @param args Arguments to forward to the sort function object, if any.
|
||||
*/
|
||||
template<typename... Comp, typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
|
||||
if(*this) {
|
||||
if constexpr(sizeof...(Comp) == 0) {
|
||||
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
|
||||
handler->sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
} else {
|
||||
auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) {
|
||||
if constexpr(sizeof...(Comp) == 1) {
|
||||
return compare((std::get<storage_type<Comp> *>(pools)->get(lhs), ...), (std::get<storage_type<Comp> *>(pools)->get(rhs), ...));
|
||||
} else {
|
||||
return compare(std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(lhs)...), std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(rhs)...));
|
||||
}
|
||||
};
|
||||
|
||||
handler->sort(std::move(comp), std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort the shared pool of entities according to the given component.
|
||||
*
|
||||
* Non-owning groups of the same type share with the registry a pool of
|
||||
* entities with its own order that doesn't depend on the order of any pool
|
||||
* of components. Users can order the underlying data structure so that it
|
||||
* respects the order of the pool of the given component.
|
||||
*
|
||||
* @note
|
||||
* The shared pool of entities and thus its order is affected by the changes
|
||||
* to each and every pool that it tracks. Therefore changes to those pools
|
||||
* can quickly ruin the order imposed to the pool of entities shared between
|
||||
* the non-owning groups.
|
||||
*
|
||||
* @tparam Comp Type of component to use to impose the order.
|
||||
*/
|
||||
template<typename Comp>
|
||||
void sort() const {
|
||||
if(*this) {
|
||||
handler->respect(*std::get<storage_type<Comp> *>(pools));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
base_type *const handler;
|
||||
const std::tuple<storage_type<Get> *...> pools;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Owning group.
|
||||
*
|
||||
* Owning groups return all entities and only the entities that have at least
|
||||
* the given components. Moreover:
|
||||
*
|
||||
* * It's guaranteed that the entity list is tightly packed in memory for fast
|
||||
* iterations.
|
||||
* * It's guaranteed that the lists of owned components are tightly packed in
|
||||
* memory for even faster iterations and to allow direct access.
|
||||
* * They stay true to the order of the owned components and all instances have
|
||||
* the same order in memory.
|
||||
*
|
||||
* The more types of components are owned by a group, the faster it is to
|
||||
* iterate them.
|
||||
*
|
||||
* @b Important
|
||||
*
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all other cases, modifying the pools iterated by the group in any way
|
||||
* invalidates all the iterators and using them results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Groups 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 all the
|
||||
* groups.
|
||||
* Moreover, sorting an owning group affects all the instance of the same group
|
||||
* (it means that users don't have to call `sort` on each instance to sort all
|
||||
* of them because they share the underlying data structure).
|
||||
*
|
||||
* @warning
|
||||
* Lifetime of a group must not overcome that of the registry that generated it.
|
||||
* In any other case, attempting to use a group results in undefined behavior.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Owned Types of components owned by the group.
|
||||
* @tparam Get Types of components observed by the group.
|
||||
* @tparam Exclude Types of components used to filter the group.
|
||||
*/
|
||||
template<typename Entity, typename... Owned, typename... Get, typename... Exclude>
|
||||
class basic_group<Entity, owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> final {
|
||||
/*! @brief A registry is allowed to create groups. */
|
||||
friend class basic_registry<Entity>;
|
||||
|
||||
template<typename Comp>
|
||||
using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>;
|
||||
|
||||
using basic_common_type = std::common_type_t<typename storage_type<Owned>::base_type..., typename storage_type<Get>::base_type...>;
|
||||
|
||||
template<typename>
|
||||
struct extended_group_iterator;
|
||||
|
||||
template<typename... OIt>
|
||||
struct extended_group_iterator<type_list<OIt...>> final {
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
extended_group_iterator() = default;
|
||||
|
||||
template<typename... Other>
|
||||
extended_group_iterator(typename basic_common_type::iterator from, const std::tuple<Other...> &other, const std::tuple<storage_type<Get> *...> &cpools) ENTT_NOEXCEPT
|
||||
: it{from},
|
||||
owned{std::get<OIt>(other)...},
|
||||
get{cpools} {}
|
||||
|
||||
extended_group_iterator &operator++() ENTT_NOEXCEPT {
|
||||
return ++it, (++std::get<OIt>(owned), ...), *this;
|
||||
}
|
||||
|
||||
extended_group_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
extended_group_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
|
||||
return std::tuple_cat(
|
||||
std::make_tuple(*it),
|
||||
std::forward_as_tuple(*std::get<OIt>(owned)...),
|
||||
std::get<storage_type<Get> *>(get)->get_as_tuple(*it)...);
|
||||
}
|
||||
|
||||
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
|
||||
return operator*();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.it == it;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
typename basic_common_type::iterator it;
|
||||
std::tuple<OIt...> owned;
|
||||
std::tuple<storage_type<Get> *...> get;
|
||||
};
|
||||
|
||||
basic_group(const std::size_t &extent, storage_type<Owned> &...opool, storage_type<Get> &...gpool) ENTT_NOEXCEPT
|
||||
: pools{&opool..., &gpool...},
|
||||
length{&extent} {}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Common type among all storage types. */
|
||||
using base_type = basic_common_type;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = typename base_type::iterator;
|
||||
/*! @brief Reversed iterator type. */
|
||||
using reverse_iterator = typename base_type::reverse_iterator;
|
||||
/*! @brief Iterable group type. */
|
||||
using iterable = iterable_adaptor<extended_group_iterator<type_list_cat_t<std::conditional_t<ignore_as_empty_v<std::remove_const_t<Owned>>, type_list<>, type_list<decltype(std::declval<storage_type<Owned>>().end())>>...>>>;
|
||||
|
||||
/*! @brief Default constructor to use to create empty, invalid groups. */
|
||||
basic_group() ENTT_NOEXCEPT
|
||||
: length{} {}
|
||||
|
||||
/**
|
||||
* @brief Returns the storage for a given component type.
|
||||
* @tparam Comp Type of component of which to return the storage.
|
||||
* @return The storage for the given component type.
|
||||
*/
|
||||
template<typename Comp>
|
||||
[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
|
||||
return *std::get<storage_type<Comp> *>(pools);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the storage for a given component type.
|
||||
* @tparam Comp Index of component of which to return the storage.
|
||||
* @return The storage for the given component type.
|
||||
*/
|
||||
template<std::size_t Comp>
|
||||
[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
|
||||
return *std::get<Comp>(pools);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of entities that have the given components.
|
||||
* @return Number of entities that have the given components.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
|
||||
return *this ? *length : size_type{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a group is empty.
|
||||
* @return True if the group is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return !*this || !*length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the group.
|
||||
*
|
||||
* The returned iterator points to the first entity of the group. If the
|
||||
* group is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the group.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
|
||||
return *this ? (std::get<0>(pools)->base_type::end() - *length) : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the group.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the group. Attempting to dereference the returned iterator results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* group.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
|
||||
return *this ? std::get<0>(pools)->base_type::end() : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the reversed group.
|
||||
*
|
||||
* The returned iterator points to the first entity of the reversed group.
|
||||
* If the group is empty, the returned iterator will be equal to `rend()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the reversed group.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
|
||||
return *this ? std::get<0>(pools)->base_type::rbegin() : reverse_iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the reversed
|
||||
* group.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the reversed group. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* reversed group.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
|
||||
return *this ? (std::get<0>(pools)->base_type::rbegin() + *length) : reverse_iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the first entity of the group, if any.
|
||||
* @return The first entity of the group if one exists, the null entity
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] entity_type front() const {
|
||||
const auto it = begin();
|
||||
return it != end() ? *it : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the last entity of the group, if any.
|
||||
* @return The last entity of the group if one exists, the null entity
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] entity_type back() const {
|
||||
const auto it = rbegin();
|
||||
return it != rend() ? *it : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds an entity.
|
||||
* @param entt A valid identifier.
|
||||
* @return An iterator to the given entity if it's found, past the end
|
||||
* iterator otherwise.
|
||||
*/
|
||||
[[nodiscard]] iterator find(const entity_type entt) const {
|
||||
const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{};
|
||||
return it != end() && it >= begin() && *it == entt ? it : end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the identifier that occupies the given position.
|
||||
* @param pos Position of the element to return.
|
||||
* @return The identifier that occupies the given position.
|
||||
*/
|
||||
[[nodiscard]] entity_type operator[](const size_type pos) const {
|
||||
return begin()[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a group is properly initialized.
|
||||
* @return True if the group is properly initialized, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return length != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a group contains an entity.
|
||||
* @param entt A valid identifier.
|
||||
* @return True if the group contains the given entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(const entity_type entt) const {
|
||||
return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
*
|
||||
* Prefer this function instead of `registry::get` during iterations. It has
|
||||
* far better performance than its counterpart.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an invalid component type results in a compilation
|
||||
* error. Attempting to use an entity that doesn't belong to the group
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @tparam Comp Types of components to get.
|
||||
* @param entt A valid identifier.
|
||||
* @return The components assigned to the entity.
|
||||
*/
|
||||
template<typename... Comp>
|
||||
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
|
||||
ENTT_ASSERT(contains(entt), "Group does not contain entity");
|
||||
|
||||
if constexpr(sizeof...(Comp) == 0) {
|
||||
return std::tuple_cat(std::get<storage_type<Owned> *>(pools)->get_as_tuple(entt)..., std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
|
||||
} else if constexpr(sizeof...(Comp) == 1) {
|
||||
return (std::get<storage_type<Comp> *>(pools)->get(entt), ...);
|
||||
} else {
|
||||
return std::tuple_cat(std::get<storage_type<Comp> *>(pools)->get_as_tuple(entt)...);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and components and applies the given function
|
||||
* object to them.
|
||||
*
|
||||
* The function object is invoked for each entity. It is provided with the
|
||||
* entity itself and a set of references to non-empty components. The
|
||||
* _constness_ of the components is as requested.<br/>
|
||||
* The signature of the function must be equivalent to one of the following
|
||||
* forms:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type, Type &...);
|
||||
* void(Type &...);
|
||||
* @endcode
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated and therefore they are never
|
||||
* returned during iterations.
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
for(auto args: each()) {
|
||||
if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) {
|
||||
std::apply(func, args);
|
||||
} else {
|
||||
std::apply([&func](auto, auto &&...less) { func(std::forward<decltype(less)>(less)...); }, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to use to _visit_ a group.
|
||||
*
|
||||
* The iterable object returns tuples that contain the current entity and a
|
||||
* set of references to its non-empty components. The _constness_ of the
|
||||
* components is as requested.
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated and therefore they are never
|
||||
* returned during iterations.
|
||||
*
|
||||
* @return An iterable object to use to _visit_ the group.
|
||||
*/
|
||||
[[nodiscard]] iterable each() const ENTT_NOEXCEPT {
|
||||
using extended_iterator_type = typename iterable::iterator;
|
||||
iterator last = length ? std::get<0>(pools)->basic_common_type::end() : iterator{};
|
||||
auto from = extended_iterator_type{last - *length, std::make_tuple((std::get<storage_type<Owned> *>(pools)->end() - *length)...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
|
||||
auto to = extended_iterator_type{last, std::make_tuple((std::get<storage_type<Owned> *>(pools)->end())...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
|
||||
return {std::move(from), std::move(to)};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort a group according to the given comparison function.
|
||||
*
|
||||
* Sort the group so that iterating it with a couple of iterators returns
|
||||
* entities and components in the expected order. See `begin` and `end` for
|
||||
* more details.
|
||||
*
|
||||
* The comparison function object must return `true` if the first element
|
||||
* is _less_ than the second one, `false` otherwise. The signature of the
|
||||
* comparison function should be equivalent to one of the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* bool(std::tuple<Component &...>, std::tuple<Component &...>);
|
||||
* bool(const Component &, const Component &);
|
||||
* bool(const Entity, const Entity);
|
||||
* @endcode
|
||||
*
|
||||
* Where `Component` are either owned types or not but still such that they
|
||||
* are iterated by the group.<br/>
|
||||
* Moreover, the comparison function object shall induce a
|
||||
* _strict weak ordering_ on the values.
|
||||
*
|
||||
* The sort function object must offer a member function template
|
||||
* `operator()` that accepts three arguments:
|
||||
*
|
||||
* * An iterator to the first element of the range to sort.
|
||||
* * An iterator past the last element of the range to sort.
|
||||
* * A comparison function to use to compare the elements.
|
||||
*
|
||||
* @tparam Comp Optional types of components to compare.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Sort Type of sort function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function object.
|
||||
* @param compare A valid comparison function object.
|
||||
* @param algo A valid sort function object.
|
||||
* @param args Arguments to forward to the sort function object, if any.
|
||||
*/
|
||||
template<typename... Comp, typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const {
|
||||
auto *cpool = std::get<0>(pools);
|
||||
|
||||
if constexpr(sizeof...(Comp) == 0) {
|
||||
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
|
||||
cpool->sort_n(*length, std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
} else {
|
||||
auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) {
|
||||
if constexpr(sizeof...(Comp) == 1) {
|
||||
return compare((std::get<storage_type<Comp> *>(pools)->get(lhs), ...), (std::get<storage_type<Comp> *>(pools)->get(rhs), ...));
|
||||
} else {
|
||||
return compare(std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(lhs)...), std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(rhs)...));
|
||||
}
|
||||
};
|
||||
|
||||
cpool->sort_n(*length, std::move(comp), std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
[this](auto *head, auto *...other) {
|
||||
for(auto next = *length; next; --next) {
|
||||
const auto pos = next - 1;
|
||||
[[maybe_unused]] const auto entt = head->data()[pos];
|
||||
(other->swap_elements(other->data()[pos], entt), ...);
|
||||
}
|
||||
}(std::get<storage_type<Owned> *>(pools)...);
|
||||
}
|
||||
|
||||
private:
|
||||
const std::tuple<storage_type<Owned> *..., storage_type<Get> *...> pools;
|
||||
const size_type *const length;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
340
src/entt/entity/handle.hpp
Normal file
340
src/entt/entity/handle.hpp
Normal file
@@ -0,0 +1,340 @@
|
||||
#ifndef ENTT_ENTITY_HANDLE_HPP
|
||||
#define ENTT_ENTITY_HANDLE_HPP
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "registry.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
template<typename Entity, typename... Type>
|
||||
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>;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename registry_type::entity_type;
|
||||
/*! @brief Underlying version type. */
|
||||
using version_type = typename registry_type::version_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename registry_type::size_type;
|
||||
|
||||
/*! @brief Constructs an invalid handle. */
|
||||
basic_handle() ENTT_NOEXCEPT
|
||||
: reg{},
|
||||
entt{null} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a handle from a given registry and entity.
|
||||
* @param ref An instance of the registry class.
|
||||
* @param value A valid identifier.
|
||||
*/
|
||||
basic_handle(registry_type &ref, entity_type value) ENTT_NOEXCEPT
|
||||
: reg{&ref},
|
||||
entt{value} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a const handle from a non-const one.
|
||||
* @tparam Other A valid entity type (see entt_traits for more details).
|
||||
* @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");
|
||||
|
||||
return reg ? basic_handle<Other, Args...>{*reg, entt} : basic_handle<Other, Args...>{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts a handle to its underlying entity.
|
||||
* @return The contained identifier.
|
||||
*/
|
||||
[[nodiscard]] operator entity_type() const ENTT_NOEXCEPT {
|
||||
return entity();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle refers to non-null registry pointer and entity.
|
||||
* @return True if the handle refers to non-null registry and entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return reg && reg->valid(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle refers to a valid entity or not.
|
||||
* @return True if the handle refers to a valid entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool valid() const {
|
||||
return reg->valid(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
return reg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the entity associated with a handle.
|
||||
* @return The entity associated with the handle.
|
||||
*/
|
||||
[[nodiscard]] entity_type entity() const ENTT_NOEXCEPT {
|
||||
return entt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys the entity associated with a handle.
|
||||
* @sa basic_registry::destroy
|
||||
*/
|
||||
void destroy() {
|
||||
reg->destroy(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @return A reference to the newly created component.
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
decltype(auto) emplace(Args &&...args) const {
|
||||
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "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.
|
||||
* @return A reference to the newly created component.
|
||||
*/
|
||||
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");
|
||||
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.
|
||||
* @return A reference to the patched component.
|
||||
*/
|
||||
template<typename Component, typename... Func>
|
||||
decltype(auto) patch(Func &&...func) const {
|
||||
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "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.
|
||||
* @return A reference to the component being replaced.
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
decltype(auto) replace(Args &&...args) const {
|
||||
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "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");
|
||||
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");
|
||||
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.
|
||||
*/
|
||||
template<typename... Component>
|
||||
[[nodiscard]] decltype(auto) all_of() const {
|
||||
return reg->template all_of<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
template<typename... Component>
|
||||
[[nodiscard]] decltype(auto) any_of() const {
|
||||
return reg->template any_of<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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");
|
||||
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.
|
||||
* @return Reference to the component owned by the 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");
|
||||
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");
|
||||
return reg->template try_get<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle has components assigned.
|
||||
* @return True if the handle has no components assigned, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool orphan() const {
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Compares two handles.
|
||||
* @tparam Args Scope of the first handle.
|
||||
* @tparam Other Scope of the second handle.
|
||||
* @param lhs A valid handle.
|
||||
* @param rhs A valid handle.
|
||||
* @return True if both handles refer to the same registry and the same
|
||||
* entity, false otherwise.
|
||||
*/
|
||||
template<typename... Args, typename... Other>
|
||||
[[nodiscard]] bool operator==(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.registry() == rhs.registry() && lhs.entity() == rhs.entity();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two handles.
|
||||
* @tparam Args Scope of the first handle.
|
||||
* @tparam Other Scope of the second handle.
|
||||
* @param lhs A valid handle.
|
||||
* @param rhs A valid handle.
|
||||
* @return False if both handles refer to the same registry and the same
|
||||
* entity, true otherwise.
|
||||
*/
|
||||
template<typename... Args, typename... Other>
|
||||
[[nodiscard]] bool operator!=(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) ENTT_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
|
||||
156
src/entt/entity/helper.hpp
Normal file
156
src/entt/entity/helper.hpp
Normal file
@@ -0,0 +1,156 @@
|
||||
#ifndef ENTT_ENTITY_HELPER_HPP
|
||||
#define ENTT_ENTITY_HELPER_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../signal/delegate.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "registry.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Converts a registry to a view.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
struct as_view {
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = std::remove_const_t<Entity>;
|
||||
/*! @brief Type of registry to convert. */
|
||||
using registry_type = constness_as_t<basic_registry<entity_type>, Entity>;
|
||||
|
||||
/**
|
||||
* @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} {}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @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{});
|
||||
}
|
||||
|
||||
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).
|
||||
*/
|
||||
template<typename Entity>
|
||||
struct as_group {
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = std::remove_const_t<Entity>;
|
||||
/*! @brief Type of registry to convert. */
|
||||
using registry_type = constness_as_t<basic_registry<entity_type>, Entity>;
|
||||
|
||||
/**
|
||||
* @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} {}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @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{});
|
||||
}
|
||||
}
|
||||
|
||||
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).
|
||||
* @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) {
|
||||
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;
|
||||
func.template connect<Member>(reg.template get<member_class_t<decltype(Member)>>(entt));
|
||||
func(reg, entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the entity associated with a given component.
|
||||
*
|
||||
* @warning
|
||||
* Currently, this function only works correctly with the default pool 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.
|
||||
* @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;
|
||||
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) {
|
||||
return *(it + dist);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
436
src/entt/entity/observer.hpp
Normal file
436
src/entt/entity/observer.hpp
Normal file
@@ -0,0 +1,436 @@
|
||||
#ifndef ENTT_ENTITY_OBSERVER_HPP
|
||||
#define ENTT_ENTITY_OBSERVER_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#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 {
|
||||
|
||||
/*! @brief Grouping matcher. */
|
||||
template<typename...>
|
||||
struct matcher {};
|
||||
|
||||
/**
|
||||
* @brief Collector.
|
||||
*
|
||||
* Primary template isn't defined on purpose. All the specializations give a
|
||||
* compile-time error, but for a few reasonable cases.
|
||||
*/
|
||||
template<typename...>
|
||||
struct basic_collector;
|
||||
|
||||
/**
|
||||
* @brief Collector.
|
||||
*
|
||||
* A collector contains a set of rules (literally, matchers) to use to track
|
||||
* entities.<br/>
|
||||
* Its main purpose is to generate a descriptor that allows an observer to know
|
||||
* how to connect to a registry.
|
||||
*/
|
||||
template<>
|
||||
struct basic_collector<> {
|
||||
/**
|
||||
* @brief Adds a grouping matcher to the collector.
|
||||
* @tparam AllOf Types of components tracked by the matcher.
|
||||
* @tparam NoneOf Types of components used to filter out entities.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>>{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds an observing matcher to the collector.
|
||||
* @tparam AnyOf Type of component for which changes should be detected.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename AnyOf>
|
||||
static constexpr auto update() ENTT_NOEXCEPT {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, AnyOf>>{};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Collector.
|
||||
* @copydetails basic_collector<>
|
||||
* @tparam Reject Untracked types used to filter out entities.
|
||||
* @tparam Require Untracked types required by the matcher.
|
||||
* @tparam Rule Specific details of the current matcher.
|
||||
* @tparam Other Other matchers.
|
||||
*/
|
||||
template<typename... Reject, typename... Require, typename... Rule, typename... Other>
|
||||
struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule...>, Other...> {
|
||||
/*! @brief Current matcher. */
|
||||
using current_type = matcher<type_list<Reject...>, type_list<Require...>, Rule...>;
|
||||
|
||||
/**
|
||||
* @brief Adds a grouping matcher to the collector.
|
||||
* @tparam AllOf Types of components tracked by the matcher.
|
||||
* @tparam NoneOf Types of components used to filter out entities.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>, current_type, Other...>{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds an observing matcher to the collector.
|
||||
* @tparam AnyOf Type of component for which changes should be detected.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename AnyOf>
|
||||
static constexpr auto update() ENTT_NOEXCEPT {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, AnyOf>, current_type, Other...>{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates the filter of the last added matcher.
|
||||
* @tparam AllOf Types of components required by the matcher.
|
||||
* @tparam NoneOf Types of components used to filter out entities.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
static constexpr auto where(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
|
||||
using extended_type = matcher<type_list<Reject..., NoneOf...>, type_list<Require..., AllOf...>, Rule...>;
|
||||
return basic_collector<extended_type, Other...>{};
|
||||
}
|
||||
};
|
||||
|
||||
/*! @brief Variable template used to ease the definition of collectors. */
|
||||
inline constexpr basic_collector<> collector{};
|
||||
|
||||
/**
|
||||
* @brief Observer.
|
||||
*
|
||||
* An observer returns all the entities and only the entities that fit the
|
||||
* requirements of at least one matcher. Moreover, it's guaranteed that the
|
||||
* entity list is tightly packed in memory for fast iterations.<br/>
|
||||
* In general, observers don't stay true to the order of any set of components.
|
||||
*
|
||||
* Observers work mainly with two types of matchers, provided through a
|
||||
* collector:
|
||||
*
|
||||
* * Observing matcher: an observer will return at least all the living entities
|
||||
* for which one or more of the given components have been updated and not yet
|
||||
* destroyed.
|
||||
* * Grouping matcher: an observer will return at least all the living entities
|
||||
* that would have entered the given group if it existed and that would have
|
||||
* not yet left it.
|
||||
*
|
||||
* If an entity respects the requirements of multiple matchers, it will be
|
||||
* returned once and only once by the observer in any case.
|
||||
*
|
||||
* Matchers support also filtering by means of a _where_ clause that accepts
|
||||
* both a list of types and an exclusion list.<br/>
|
||||
* Whenever a matcher finds that an entity matches its requirements, the
|
||||
* condition of the filter is verified before to register the entity itself.
|
||||
* Moreover, a registered entity isn't returned by the observer if the condition
|
||||
* set by the filter is broken in the meantime.
|
||||
*
|
||||
* @b Important
|
||||
*
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all the other cases, modifying the pools of the given components in any
|
||||
* way invalidates all the iterators and using them results in undefined
|
||||
* behavior.
|
||||
*
|
||||
* @warning
|
||||
* Lifetime of an observer doesn't necessarily have to overcome that of the
|
||||
* registry to which it is connected. However, the observer must be disconnected
|
||||
* from the registry before being destroyed to avoid crashes due to dangling
|
||||
* pointers.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
class basic_observer {
|
||||
using payload_type = std::uint32_t;
|
||||
|
||||
template<typename>
|
||||
struct matcher_handler;
|
||||
|
||||
template<typename... Reject, typename... Require, typename AnyOf>
|
||||
struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, AnyOf>> {
|
||||
template<std::size_t Index>
|
||||
static void maybe_valid_if(basic_observer &obs, basic_registry<Entity> ®, const Entity entt) {
|
||||
if(reg.template all_of<Require...>(entt) && !reg.template any_of<Reject...>(entt)) {
|
||||
if(!obs.storage.contains(entt)) {
|
||||
obs.storage.emplace(entt);
|
||||
}
|
||||
|
||||
obs.storage.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);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void connect(basic_observer &obs, basic_registry<Entity> ®) {
|
||||
(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);
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
auto condition = [®, entt]() {
|
||||
if constexpr(sizeof...(Ignore) == 0) {
|
||||
return reg.template all_of<AllOf..., Require...>(entt) && !reg.template any_of<NoneOf..., Reject...>(entt);
|
||||
} else {
|
||||
return reg.template all_of<AllOf..., Require...>(entt) && ((std::is_same_v<Ignore..., NoneOf> || !reg.template any_of<NoneOf>(entt)) && ...) && !reg.template any_of<Reject...>(entt);
|
||||
}
|
||||
};
|
||||
|
||||
if(condition()) {
|
||||
if(!obs.storage.contains(entt)) {
|
||||
obs.storage.emplace(entt);
|
||||
}
|
||||
|
||||
obs.storage.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);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void connect(basic_observer &obs, basic_registry<Entity> ®) {
|
||||
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
|
||||
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
|
||||
(reg.template on_construct<AllOf>().template connect<&maybe_valid_if<Index>>(obs), ...);
|
||||
(reg.template on_destroy<NoneOf>().template connect<&maybe_valid_if<Index, NoneOf>>(obs), ...);
|
||||
(reg.template on_destroy<AllOf>().template connect<&discard_if<Index>>(obs), ...);
|
||||
(reg.template on_construct<NoneOf>().template connect<&discard_if<Index>>(obs), ...);
|
||||
}
|
||||
|
||||
static void disconnect(basic_observer &obs, 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), ...);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Matcher>
|
||||
static void disconnect(basic_registry<Entity> ®, 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");
|
||||
(matcher_handler<Matcher>::template connect<Index>(*this, reg), ...);
|
||||
release.template connect<&basic_observer::disconnect<Matcher...>>(reg);
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = typename basic_sparse_set<Entity>::iterator;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_observer()
|
||||
: release{},
|
||||
storage{} {}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_observer(const basic_observer &) = delete;
|
||||
/*! @brief Default move constructor, deleted on purpose. */
|
||||
basic_observer(basic_observer &&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Creates an observer and connects it to a given registry.
|
||||
* @tparam Matcher Types of matchers to use to initialize the observer.
|
||||
* @param reg A valid reference to a registry.
|
||||
*/
|
||||
template<typename... Matcher>
|
||||
basic_observer(basic_registry<entity_type> ®, basic_collector<Matcher...>)
|
||||
: basic_observer{} {
|
||||
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.
|
||||
*/
|
||||
basic_observer &operator=(const basic_observer &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator, deleted on purpose.
|
||||
* @return This observer.
|
||||
*/
|
||||
basic_observer &operator=(basic_observer &&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Connects an observer to a given registry.
|
||||
* @tparam Matcher Types of matchers to use to initialize the observer.
|
||||
* @param reg A valid reference to a registry.
|
||||
*/
|
||||
template<typename... Matcher>
|
||||
void connect(basic_registry<entity_type> ®, basic_collector<Matcher...>) {
|
||||
disconnect();
|
||||
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
|
||||
storage.clear();
|
||||
}
|
||||
|
||||
/*! @brief Disconnects an observer from the registry it keeps track of. */
|
||||
void disconnect() {
|
||||
if(release) {
|
||||
release(*this);
|
||||
release.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements in an observer.
|
||||
* @return Number of elements.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
|
||||
return storage.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();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Direct access to the list of entities of the observer.
|
||||
*
|
||||
* The returned pointer is such that range `[data(), data() + size())` is
|
||||
* always a valid range, even if the container is empty.
|
||||
*
|
||||
* @note
|
||||
* Entities are in the reverse order as returned by the `begin`/`end`
|
||||
* iterators.
|
||||
*
|
||||
* @return A pointer to the array of entities.
|
||||
*/
|
||||
[[nodiscard]] const entity_type *data() const ENTT_NOEXCEPT {
|
||||
return storage.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()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the observer.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
|
||||
return storage.basic_sparse_set<entity_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();
|
||||
}
|
||||
|
||||
/*! @brief Clears the underlying container. */
|
||||
void clear() ENTT_NOEXCEPT {
|
||||
storage.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and applies the given function object to them.
|
||||
*
|
||||
* The function object is invoked for each entity.<br/>
|
||||
* The signature of the function must be equivalent to the following form:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
for(const auto entity: *this) {
|
||||
func(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and applies the given function object to them,
|
||||
* then clears the observer.
|
||||
*
|
||||
* @sa each
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) {
|
||||
std::as_const(*this).each(std::move(func));
|
||||
clear();
|
||||
}
|
||||
|
||||
private:
|
||||
delegate<void(basic_observer &)> release;
|
||||
basic_storage<entity_type, payload_type> storage;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
487
src/entt/entity/organizer.hpp
Normal file
487
src/entt/entity/organizer.hpp
Normal file
@@ -0,0 +1,487 @@
|
||||
#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 "fwd.hpp"
|
||||
#include "helper.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
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 Type>
|
||||
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<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<Type>,
|
||||
type_list<>>;
|
||||
};
|
||||
|
||||
template<typename Entity, typename... Override>
|
||||
struct unpack_type<basic_registry<Entity>, 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 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 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, typename>
|
||||
struct resource;
|
||||
|
||||
template<typename... Args, typename... Req>
|
||||
struct resource<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...));
|
||||
|
||||
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...));
|
||||
|
||||
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...));
|
||||
|
||||
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();
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Utility class for creating a static task graph.
|
||||
*
|
||||
* This class offers minimal support (but sufficient in many cases) for creating
|
||||
* an execution graph from functions and their requirements on resources.<br/>
|
||||
* Note that the resulting tasks aren't executed in any case. This isn't the
|
||||
* 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).
|
||||
*/
|
||||
template<typename Entity>
|
||||
class basic_organizer final {
|
||||
using callback_type = void(const void *, basic_registry<Entity> &);
|
||||
using prepare_type = void(basic_registry<Entity> &);
|
||||
using dependency_type = std::size_t(const bool, const type_info **, const std::size_t);
|
||||
|
||||
struct vertex_data final {
|
||||
std::size_t ro_count{};
|
||||
std::size_t rw_count{};
|
||||
const char *name{};
|
||||
const void *payload{};
|
||||
callback_type *callback{};
|
||||
dependency_type *dependency;
|
||||
prepare_type *prepare{};
|
||||
const type_info *info{};
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] static decltype(auto) extract(basic_registry<Entity> ®) {
|
||||
if constexpr(std::is_same_v<Type, basic_registry<Entity>>) {
|
||||
return reg;
|
||||
} else if constexpr(internal::is_view_v<Type>) {
|
||||
return as_view{reg};
|
||||
} else {
|
||||
return reg.template ctx_or_set<std::remove_reference_t<Type>>();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
[[nodiscard]] static auto to_args(basic_registry<Entity> ®, type_list<Args...>) {
|
||||
return std::tuple<decltype(extract<Args>(reg))...>(extract<Args>(reg)...);
|
||||
}
|
||||
|
||||
template<typename... Type>
|
||||
static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) {
|
||||
if constexpr(sizeof...(Type) == 0u) {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Raw task function type. */
|
||||
using function_type = callback_type;
|
||||
|
||||
/*! @brief Vertex type of a task graph defined as an adjacency list. */
|
||||
struct vertex {
|
||||
/**
|
||||
* @brief Constructs a vertex of the task graph.
|
||||
* @param vtype True if the vertex is a top-level one, false otherwise.
|
||||
* @param data The data associated with the vertex.
|
||||
* @param edges The indices of the children in the adjacency list.
|
||||
*/
|
||||
vertex(const bool vtype, vertex_data data, std::vector<std::size_t> edges)
|
||||
: is_top_level{vtype},
|
||||
node{std::move(data)},
|
||||
reachable{std::move(edges)} {}
|
||||
|
||||
/**
|
||||
* @brief Fills a buffer with the type info objects for the writable
|
||||
* resources of a vertex.
|
||||
* @param buffer A buffer pre-allocated by the user.
|
||||
* @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 {
|
||||
return node.dependency(false, buffer, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fills a buffer with the type info objects for the read-only
|
||||
* resources of a vertex.
|
||||
* @param buffer A buffer pre-allocated by the user.
|
||||
* @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 {
|
||||
return node.dependency(true, buffer, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
return node.ro_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
return node.rw_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
return is_top_level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a type info object associated with a vertex.
|
||||
* @return A properly initialized type info object.
|
||||
*/
|
||||
const type_info &info() const ENTT_NOEXCEPT {
|
||||
return *node.info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
return node.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the function associated with a vertex.
|
||||
* @return The function associated with the vertex.
|
||||
*/
|
||||
function_type *callback() const ENTT_NOEXCEPT {
|
||||
return node.callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
return node.payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
return reachable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepares a registry and assures that all required resources
|
||||
* are properly instantiated before using them.
|
||||
* @param reg A valid registry.
|
||||
*/
|
||||
void prepare(basic_registry<entity_type> ®) const {
|
||||
node.prepare ? node.prepare(reg) : void();
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_top_level;
|
||||
vertex_data node;
|
||||
std::vector<std::size_t> reachable;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Adds a free function to the task list.
|
||||
* @tparam Candidate Function to add to the task list.
|
||||
* @tparam Req Additional requirements and/or override resource access mode.
|
||||
* @param name Optional name to associate with the task.
|
||||
*/
|
||||
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>>;
|
||||
|
||||
callback_type *callback = +[](const void *, basic_registry<entity_type> ®) {
|
||||
std::apply(Candidate, to_args(reg, typename resource_type::args{}));
|
||||
};
|
||||
|
||||
vertex_data vdata{
|
||||
resource_type::ro::size,
|
||||
resource_type::rw::size,
|
||||
name,
|
||||
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{})); },
|
||||
&type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
|
||||
|
||||
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
vertices.push_back(std::move(vdata));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a free function with payload or a member function with an
|
||||
* instance to the task list.
|
||||
* @tparam Candidate Function or member to add to the task list.
|
||||
* @tparam Req Additional requirements and/or override resource access mode.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid object that fits the purpose.
|
||||
* @param name Optional name to associate with the task.
|
||||
*/
|
||||
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>>;
|
||||
|
||||
callback_type *callback = +[](const void *payload, basic_registry<entity_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{})));
|
||||
};
|
||||
|
||||
vertex_data vdata{
|
||||
resource_type::ro::size,
|
||||
resource_type::rw::size,
|
||||
name,
|
||||
&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{})); },
|
||||
&type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
|
||||
|
||||
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
vertices.push_back(std::move(vdata));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds an user defined function with optional payload to the task
|
||||
* list.
|
||||
* @tparam Req Additional requirements and/or override resource access mode.
|
||||
* @param func Function to add to the task list.
|
||||
* @param payload User defined arbitrary data.
|
||||
* @param name Optional name to associate with the task.
|
||||
*/
|
||||
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...>>;
|
||||
track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
|
||||
vertex_data vdata{
|
||||
resource_type::ro::size,
|
||||
resource_type::rw::size,
|
||||
name,
|
||||
payload,
|
||||
func,
|
||||
+[](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); },
|
||||
nullptr,
|
||||
&type_id<void>()};
|
||||
|
||||
vertices.push_back(std::move(vdata));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generates a task graph for the current content.
|
||||
* @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());
|
||||
|
||||
for(std::size_t col{}, length = vertices.size(); col < length; ++col) {
|
||||
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(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));
|
||||
}
|
||||
|
||||
return adjacency_list;
|
||||
}
|
||||
|
||||
/*! @brief Erases all elements from a container. */
|
||||
void clear() {
|
||||
dependencies.clear();
|
||||
vertices.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
dense_hash_map<id_type, std::vector<std::pair<std::size_t, bool>>, identity> dependencies;
|
||||
std::vector<vertex_data> vertices;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
1548
src/entt/entity/registry.hpp
Normal file
1548
src/entt/entity/registry.hpp
Normal file
File diff suppressed because it is too large
Load Diff
249
src/entt/entity/runtime_view.hpp
Normal file
249
src/entt/entity/runtime_view.hpp
Normal file
@@ -0,0 +1,249 @@
|
||||
#ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP
|
||||
#define ENTT_ENTITY_RUNTIME_VIEW_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#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.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Type>
|
||||
class runtime_view_iterator final {
|
||||
[[nodiscard]] bool valid() const {
|
||||
return 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;
|
||||
|
||||
runtime_view_iterator(const std::vector<const Type *> &cpools, const std::vector<const Type *> &ignore, iterator_type curr) ENTT_NOEXCEPT
|
||||
: pools{&cpools},
|
||||
filter{&ignore},
|
||||
it{curr} {
|
||||
if(it != (*pools)[0]->end() && !valid()) {
|
||||
++(*this);
|
||||
}
|
||||
}
|
||||
|
||||
runtime_view_iterator &operator++() {
|
||||
while(++it != (*pools)[0]->end() && !valid()) {}
|
||||
return *this;
|
||||
}
|
||||
|
||||
runtime_view_iterator operator++(int) {
|
||||
runtime_view_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
runtime_view_iterator &operator--() ENTT_NOEXCEPT {
|
||||
while(--it != (*pools)[0]->begin() && !valid()) {}
|
||||
return *this;
|
||||
}
|
||||
|
||||
runtime_view_iterator operator--(int) ENTT_NOEXCEPT {
|
||||
runtime_view_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] pointer operator->() const {
|
||||
return it.operator->();
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const runtime_view_iterator &other) const ENTT_NOEXCEPT {
|
||||
return it == other.it;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator!=(const runtime_view_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<const Type *> *pools;
|
||||
const std::vector<const Type *> *filter;
|
||||
iterator_type it;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief 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.
|
||||
*
|
||||
* @b Important
|
||||
*
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all the other cases, modifying the pools of the given components in any
|
||||
* way invalidates all the iterators and using them results in undefined
|
||||
* behavior.
|
||||
*
|
||||
* @note
|
||||
* Views share references to the underlying data structures of the registry that
|
||||
* generated them. Therefore any change to the entities and to the components
|
||||
* made by means of the registry are immediately reflected by the views, unless
|
||||
* a pool was missing when the view was built (in this case, the view won't
|
||||
* have a valid reference and won't be updated accordingly).
|
||||
*
|
||||
* @warning
|
||||
* Lifetime of a view must not overcome that of the registry that generated it.
|
||||
* In any other case, attempting to use a view results in undefined behavior.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Bidirectional iterator type. */
|
||||
using iterator = internal::runtime_view_iterator<basic_common_type>;
|
||||
|
||||
/*! @brief Default constructor to use to create empty, invalid views. */
|
||||
basic_runtime_view() ENTT_NOEXCEPT
|
||||
: pools{},
|
||||
filter{} {}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
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());
|
||||
});
|
||||
|
||||
// brings the best candidate (if any) on front of the vector
|
||||
std::rotate(pools.begin(), candidate, pools.end());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Estimates the number of entities iterated by the view.
|
||||
* @return Estimated number of entities iterated by the view.
|
||||
*/
|
||||
[[nodiscard]] size_type size_hint() const {
|
||||
return valid() ? pools.front()->size() : size_type{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @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()`.
|
||||
*
|
||||
* @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{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @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{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a view contains an entity.
|
||||
* @param entt A valid identifier.
|
||||
* @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); })
|
||||
&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); });
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
for(const auto entity: *this) {
|
||||
func(entity);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<const basic_common_type *> pools;
|
||||
std::vector<const basic_common_type *> filter;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
562
src/entt/entity/snapshot.hpp
Normal file
562
src/entt/entity/snapshot.hpp
Normal file
@@ -0,0 +1,562 @@
|
||||
#ifndef ENTT_ENTITY_SNAPSHOT_HPP
|
||||
#define ENTT_ENTITY_SNAPSHOT_HPP
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../container/dense_hash_map.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "component.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "registry.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Utility class to create snapshots from a registry.
|
||||
*
|
||||
* A _snapshot_ can be either a dump of the entire registry or a narrower
|
||||
* selection of components of interest.<br/>
|
||||
* 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).
|
||||
*/
|
||||
template<typename Entity>
|
||||
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), ...);
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
|
||||
/**
|
||||
* @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
|
||||
: reg{&source} {}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
basic_snapshot(basic_snapshot &&) = default;
|
||||
|
||||
/*! @brief Default move assignment operator. @return This snapshot. */
|
||||
basic_snapshot &operator=(basic_snapshot &&) = 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.
|
||||
*
|
||||
* @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 Archive>
|
||||
const basic_snapshot &entities(Archive &archive) const {
|
||||
const auto sz = reg->size();
|
||||
|
||||
archive(typename entity_traits::entity_type(sz + 1u));
|
||||
archive(reg->released());
|
||||
|
||||
for(auto first = reg->data(), last = first + sz; first != last; ++first) {
|
||||
archive(*first);
|
||||
}
|
||||
|
||||
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.
|
||||
* @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.
|
||||
* @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...>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
const basic_registry<entity_type> *reg;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Utility class to restore a snapshot as a whole.
|
||||
*
|
||||
* A snapshot loader requires that the destination registry be empty and loads
|
||||
* all the data at once while keeping intact the identifiers that the entities
|
||||
* 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).
|
||||
*/
|
||||
template<typename Entity>
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
|
||||
/**
|
||||
* @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
|
||||
: reg{&source} {
|
||||
// restoring a snapshot as a whole requires a clean registry
|
||||
ENTT_ASSERT(reg->empty(), "Registry must be empty");
|
||||
}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
basic_snapshot_loader(basic_snapshot_loader &&) = default;
|
||||
|
||||
/*! @brief Default move assignment operator. @return This loader. */
|
||||
basic_snapshot_loader &operator=(basic_snapshot_loader &&) = 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.
|
||||
*
|
||||
* @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 Archive>
|
||||
const basic_snapshot_loader &entities(Archive &archive) const {
|
||||
typename entity_traits::entity_type length{};
|
||||
|
||||
archive(length);
|
||||
std::vector<entity_type> all(length);
|
||||
|
||||
for(std::size_t pos{}; pos < length; ++pos) {
|
||||
archive(all[pos]);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys those entities that have no components.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
});
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
basic_registry<entity_type> *reg;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Utility class for _continuous loading_.
|
||||
*
|
||||
* A _continuous loader_ is designed to load data from a source registry to a
|
||||
* (possibly) non-empty destination. The loader can accommodate in a registry
|
||||
* more than one snapshot in a sort of _continuous loading_ that updates the
|
||||
* destination one step at a time.<br/>
|
||||
* 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
|
||||
* the requirement of transferring somehow parts of the representation side to
|
||||
* side.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
class basic_continuous_loader {
|
||||
using entity_traits = entt_traits<Entity>;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
// set the dirty flag
|
||||
remloc[entt].second = true;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
auto update(int, Container &container)
|
||||
-> decltype(typename Container::mapped_type{}, void()) {
|
||||
// map like container
|
||||
Container other;
|
||||
|
||||
for(auto &&pair: container) {
|
||||
using first_type = std::remove_const_t<typename std::decay_t<decltype(pair)>::first_type>;
|
||||
using second_type = typename std::decay_t<decltype(pair)>::second_type;
|
||||
|
||||
if constexpr(std::is_same_v<first_type, entity_type> && std::is_same_v<second_type, entity_type>) {
|
||||
other.emplace(map(pair.first), map(pair.second));
|
||||
} else if constexpr(std::is_same_v<first_type, entity_type>) {
|
||||
other.emplace(map(pair.first), std::move(pair.second));
|
||||
} else {
|
||||
static_assert(std::is_same_v<second_type, entity_type>, "Neither the key nor the value are of entity type");
|
||||
other.emplace(std::move(pair.first), map(pair.second));
|
||||
}
|
||||
}
|
||||
|
||||
std::swap(container, other);
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
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");
|
||||
|
||||
for(auto &&entt: container) {
|
||||
entt = map(entt);
|
||||
}
|
||||
}
|
||||
|
||||
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>) {
|
||||
return;
|
||||
} else if constexpr(std::is_same_v<Member, entity_type>) {
|
||||
instance.*member = map(instance.*member);
|
||||
} else {
|
||||
// maybe a container? let's try...
|
||||
update(0, instance.*member);
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
|
||||
/**
|
||||
* @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} {}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
basic_continuous_loader(basic_continuous_loader &&) = default;
|
||||
|
||||
/*! @brief Default move assignment operator. @return This loader. */
|
||||
basic_continuous_loader &operator=(basic_continuous_loader &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Restores entities that were in use during serialization.
|
||||
*
|
||||
* This function restores the entities that were in use during serialization
|
||||
* and creates local counterparts for them if required.
|
||||
*
|
||||
* @tparam Archive Type of input archive.
|
||||
* @param archive A valid reference to an input archive.
|
||||
* @return A non-const reference to this loader.
|
||||
*/
|
||||
template<typename Archive>
|
||||
basic_continuous_loader &entities(Archive &archive) {
|
||||
typename entity_traits::entity_type length{};
|
||||
entity_type entt{};
|
||||
|
||||
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(const auto entity = entity_traits::to_entity(entt); entity == pos) {
|
||||
restore(entt);
|
||||
} else {
|
||||
destroy(entt);
|
||||
}
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
it = remloc.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys those entities that have no components.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
});
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tests if a loader knows about a given entity.
|
||||
* @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());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the identifier to which an entity refers.
|
||||
* @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;
|
||||
}
|
||||
|
||||
return other;
|
||||
}
|
||||
|
||||
private:
|
||||
dense_hash_map<entity_type, std::pair<entity_type, bool>> remloc;
|
||||
basic_registry<entity_type> *reg;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
935
src/entt/entity/sparse_set.hpp
Normal file
935
src/entt/entity/sparse_set.hpp
Normal file
@@ -0,0 +1,935 @@
|
||||
#ifndef ENTT_ENTITY_SPARSE_SET_HPP
|
||||
#define ENTT_ENTITY_SPARSE_SET_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/algorithm.hpp"
|
||||
#include "../core/any.hpp"
|
||||
#include "../core/memory.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Container>
|
||||
struct sparse_set_iterator final {
|
||||
using value_type = typename Container::value_type;
|
||||
using pointer = typename Container::const_pointer;
|
||||
using reference = typename Container::const_reference;
|
||||
using difference_type = typename Container::difference_type;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
sparse_set_iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
sparse_set_iterator(const Container &ref, const difference_type idx) ENTT_NOEXCEPT
|
||||
: packed{std::addressof(ref)},
|
||||
offset{idx} {}
|
||||
|
||||
sparse_set_iterator &operator++() ENTT_NOEXCEPT {
|
||||
return --offset, *this;
|
||||
}
|
||||
|
||||
sparse_set_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
sparse_set_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
sparse_set_iterator &operator--() ENTT_NOEXCEPT {
|
||||
return ++offset, *this;
|
||||
}
|
||||
|
||||
sparse_set_iterator operator--(int) ENTT_NOEXCEPT {
|
||||
sparse_set_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
sparse_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
|
||||
offset -= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
sparse_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
||||
sparse_set_iterator copy = *this;
|
||||
return (copy += value);
|
||||
}
|
||||
|
||||
sparse_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
|
||||
return (*this += -value);
|
||||
}
|
||||
|
||||
sparse_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return (*this + -value);
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator[](const difference_type value) const {
|
||||
const auto pos = offset - value - 1;
|
||||
return packed->data()[pos];
|
||||
}
|
||||
|
||||
[[nodiscard]] pointer operator->() const {
|
||||
const auto pos = offset - 1;
|
||||
return packed->data() + pos;
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
[[nodiscard]] difference_type index() const ENTT_NOEXCEPT {
|
||||
return offset;
|
||||
}
|
||||
|
||||
private:
|
||||
const Container *packed;
|
||||
difference_type offset;
|
||||
};
|
||||
|
||||
template<typename Type, typename Other>
|
||||
[[nodiscard]] auto operator-(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
|
||||
return rhs.index() - lhs.index();
|
||||
}
|
||||
|
||||
template<typename Type, typename Other>
|
||||
[[nodiscard]] bool operator==(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.index() == rhs.index();
|
||||
}
|
||||
|
||||
template<typename Type, typename Other>
|
||||
[[nodiscard]] bool operator!=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<typename Type, typename Other>
|
||||
[[nodiscard]] bool operator<(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.index() > rhs.index();
|
||||
}
|
||||
|
||||
template<typename Type, typename Other>
|
||||
[[nodiscard]] bool operator>(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.index() < rhs.index();
|
||||
}
|
||||
|
||||
template<typename Type, typename Other>
|
||||
[[nodiscard]] bool operator<=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs > rhs);
|
||||
}
|
||||
|
||||
template<typename Type, typename Other>
|
||||
[[nodiscard]] bool operator>=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/*! @brief Sparse set deletion policy. */
|
||||
enum class deletion_policy : std::uint8_t {
|
||||
/*! @brief Swap-and-pop deletion policy. */
|
||||
swap_and_pop = 0u,
|
||||
/*! @brief In-place deletion policy. */
|
||||
in_place = 1u
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Basic sparse set implementation.
|
||||
*
|
||||
* Sparse set or packed array or whatever is the name users give it.<br/>
|
||||
* Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a
|
||||
* _packed_ one; one used for direct access through contiguous memory, the other
|
||||
* one used to get the data through an extra level of indirection.<br/>
|
||||
* This is largely used by the registry to offer users the fastest access ever
|
||||
* to the components. Views and groups in general are almost entirely designed
|
||||
* around sparse sets.
|
||||
*
|
||||
* This type of data structure is widely documented in the literature and on the
|
||||
* web. This is nothing more than a customized implementation suitable for the
|
||||
* purpose of the framework.
|
||||
*
|
||||
* @note
|
||||
* Internal data structures arrange elements to maximize performance. There are
|
||||
* no guarantees that entities are returned in the insertion order when iterate
|
||||
* a sparse set. Do not make assumption on the order in any case.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Entity, typename Allocator>
|
||||
class basic_sparse_set {
|
||||
using allocator_traits = std::allocator_traits<Allocator>;
|
||||
using alloc = typename allocator_traits::template rebind_alloc<Entity>;
|
||||
using alloc_traits = typename std::allocator_traits<alloc>;
|
||||
|
||||
using entity_traits = entt_traits<Entity>;
|
||||
using sparse_container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
|
||||
using packed_container_type = std::vector<Entity, alloc>;
|
||||
|
||||
[[nodiscard]] auto sparse_ptr(const Entity entt) const {
|
||||
const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
|
||||
const auto page = pos / entity_traits::page_size;
|
||||
return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, entity_traits::page_size)) : nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto &sparse_ref(const Entity entt) const {
|
||||
ENTT_ASSERT(sparse_ptr(entt), "Invalid element");
|
||||
const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
|
||||
return sparse[pos / entity_traits::page_size][fast_mod(pos, entity_traits::page_size)];
|
||||
}
|
||||
|
||||
void release_sparse_pages() {
|
||||
auto page_allocator{packed.get_allocator()};
|
||||
|
||||
for(auto &&page: sparse) {
|
||||
if(page != nullptr) {
|
||||
std::destroy(page, page + entity_traits::page_size);
|
||||
alloc_traits::deallocate(page_allocator, page, entity_traits::page_size);
|
||||
page = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Returns the element assigned to an entity.
|
||||
* @return An opaque pointer to the element assigned to the entity.
|
||||
*/
|
||||
virtual const void *get_at(const std::size_t) const ENTT_NOEXCEPT {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*! @brief Swaps two entities in a sparse set. */
|
||||
virtual void swap_at(const std::size_t, const std::size_t) {}
|
||||
|
||||
/*! @brief Moves an entity in a sparse set. */
|
||||
virtual void move_and_pop(const std::size_t, const std::size_t) {}
|
||||
|
||||
/**
|
||||
* @brief Erase an entity from a sparse set.
|
||||
* @param entt A valid identifier.
|
||||
*/
|
||||
virtual void swap_and_pop(const Entity entt) {
|
||||
auto &ref = sparse_ref(entt);
|
||||
const auto pos = static_cast<size_type>(entity_traits::to_entity(ref));
|
||||
ENTT_ASSERT(packed[pos] == entt, "Invalid identifier");
|
||||
|
||||
packed[pos] = packed.back();
|
||||
auto &elem = sparse_ref(packed.back());
|
||||
elem = entity_traits::combine(entity_traits::to_integral(ref), entity_traits::to_integral(elem));
|
||||
// lazy self-assignment guard
|
||||
ref = null;
|
||||
// unnecessary but it helps to detect nasty bugs
|
||||
ENTT_ASSERT((packed.back() = tombstone, true), "");
|
||||
packed.pop_back();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erase an entity from a sparse set.
|
||||
* @param entt A valid identifier.
|
||||
*/
|
||||
virtual void in_place_pop(const Entity entt) {
|
||||
auto &ref = sparse_ref(entt);
|
||||
const auto pos = static_cast<size_type>(entity_traits::to_entity(ref));
|
||||
ENTT_ASSERT(packed[pos] == entt, "Invalid identifier");
|
||||
|
||||
packed[pos] = std::exchange(free_list, entity_traits::combine(static_cast<typename entity_traits::entity_type>(pos), entity_traits::reserved));
|
||||
// lazy self-assignment guard
|
||||
ref = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns an entity to a sparse set.
|
||||
* @param entt A valid identifier.
|
||||
*/
|
||||
virtual void try_emplace(const Entity entt, const void * = nullptr) {
|
||||
const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
|
||||
const auto page = pos / entity_traits::page_size;
|
||||
|
||||
if(!(page < sparse.size())) {
|
||||
sparse.resize(page + 1u, nullptr);
|
||||
}
|
||||
|
||||
if(!sparse[page]) {
|
||||
auto page_allocator{packed.get_allocator()};
|
||||
sparse[page] = alloc_traits::allocate(page_allocator, entity_traits::page_size);
|
||||
std::uninitialized_fill(sparse[page], sparse[page] + entity_traits::page_size, null);
|
||||
}
|
||||
|
||||
auto &elem = sparse[page][fast_mod(pos, entity_traits::page_size)];
|
||||
ENTT_ASSERT(entity_traits::to_version(elem) == entity_traits::to_version(tombstone), "Slot not available");
|
||||
|
||||
if(free_list == null) {
|
||||
elem = entity_traits::combine(static_cast<typename entity_traits::entity_type>(packed.size()), entity_traits::to_integral(entt));
|
||||
packed.push_back(entt);
|
||||
} else {
|
||||
elem = entity_traits::combine(entity_traits::to_integral(free_list), entity_traits::to_integral(entt));
|
||||
free_list = std::exchange(packed[static_cast<size_type>(entity_traits::to_entity(free_list))], entt);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Underlying version type. */
|
||||
using version_type = typename entity_traits::version_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename packed_container_type::size_type;
|
||||
/*! @brief Pointer type to contained entities. */
|
||||
using pointer = typename packed_container_type::const_pointer;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = internal::sparse_set_iterator<packed_container_type>;
|
||||
/*! @brief Constant random access iterator type. */
|
||||
using const_iterator = iterator;
|
||||
/*! @brief Reverse iterator type. */
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
/*! @brief Constant reverse iterator type. */
|
||||
using const_reverse_iterator = reverse_iterator;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_sparse_set()
|
||||
: basic_sparse_set{type_id<void>()} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_sparse_set(const allocator_type &allocator)
|
||||
: basic_sparse_set{type_id<void>(), deletion_policy::swap_and_pop, allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with the given policy and allocator.
|
||||
* @param pol Type of deletion policy.
|
||||
* @param allocator The allocator to use (possibly default-constructed).
|
||||
*/
|
||||
explicit basic_sparse_set(deletion_policy pol, const allocator_type &allocator = {})
|
||||
: basic_sparse_set{type_id<void>(), pol, allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with the given policy and allocator.
|
||||
* @param value Returned value type, if any.
|
||||
* @param pol Type of deletion policy.
|
||||
* @param allocator The allocator to use (possibly default-constructed).
|
||||
*/
|
||||
explicit basic_sparse_set(const type_info &value, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {})
|
||||
: sparse{allocator},
|
||||
packed{allocator},
|
||||
info{&value},
|
||||
free_list{tombstone},
|
||||
mode{pol} {}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_sparse_set(basic_sparse_set &&other) ENTT_NOEXCEPT
|
||||
: sparse{std::move(other.sparse)},
|
||||
packed{std::move(other.packed)},
|
||||
info{other.info},
|
||||
free_list{std::exchange(other.free_list, tombstone)},
|
||||
mode{other.mode} {}
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) ENTT_NOEXCEPT
|
||||
: sparse{std::move(other.sparse), allocator},
|
||||
packed{std::move(other.packed), allocator},
|
||||
info{other.info},
|
||||
free_list{std::exchange(other.free_list, tombstone)},
|
||||
mode{other.mode} {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed");
|
||||
}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
virtual ~basic_sparse_set() {
|
||||
release_sparse_pages();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This sparse set.
|
||||
*/
|
||||
basic_sparse_set &operator=(basic_sparse_set &&other) ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed");
|
||||
|
||||
release_sparse_pages();
|
||||
sparse = std::move(other.sparse);
|
||||
packed = std::move(other.packed);
|
||||
info = other.info;
|
||||
free_list = std::exchange(other.free_list, tombstone);
|
||||
mode = other.mode;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given sparse set.
|
||||
* @param other Sparse set to exchange the content with.
|
||||
*/
|
||||
void swap(basic_sparse_set &other) {
|
||||
using std::swap;
|
||||
swap(sparse, other.sparse);
|
||||
swap(packed, other.packed);
|
||||
swap(info, other.info);
|
||||
swap(free_list, other.free_list);
|
||||
swap(mode, other.mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
|
||||
return packed.get_allocator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the deletion policy of a sparse set.
|
||||
* @return The deletion policy of the sparse set.
|
||||
*/
|
||||
[[nodiscard]] deletion_policy policy() const ENTT_NOEXCEPT {
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the next slot available for insertion.
|
||||
* @return The next slot available for insertion.
|
||||
*/
|
||||
[[nodiscard]] size_type slot() const ENTT_NOEXCEPT {
|
||||
return free_list == null ? packed.size() : static_cast<size_type>(entity_traits::to_entity(free_list));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Increases the capacity of a sparse set.
|
||||
*
|
||||
* If the new capacity is greater than the current capacity, new storage is
|
||||
* allocated, otherwise the method does nothing.
|
||||
*
|
||||
* @param cap Desired capacity.
|
||||
*/
|
||||
virtual void reserve(const size_type cap) {
|
||||
packed.reserve(cap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements that a sparse set has currently
|
||||
* allocated space for.
|
||||
* @return Capacity of the sparse set.
|
||||
*/
|
||||
[[nodiscard]] virtual size_type capacity() const ENTT_NOEXCEPT {
|
||||
return packed.capacity();
|
||||
}
|
||||
|
||||
/*! @brief Requests the removal of unused capacity. */
|
||||
virtual void shrink_to_fit() {
|
||||
packed.shrink_to_fit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the extent of a sparse set.
|
||||
*
|
||||
* The extent of a sparse set is also the size of the internal sparse array.
|
||||
* There is no guarantee that the internal packed array has the same size.
|
||||
* Usually the size of the internal sparse array is equal or greater than
|
||||
* the one of the internal packed array.
|
||||
*
|
||||
* @return Extent of the sparse set.
|
||||
*/
|
||||
[[nodiscard]] size_type extent() const ENTT_NOEXCEPT {
|
||||
return sparse.size() * entity_traits::page_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements in a sparse set.
|
||||
*
|
||||
* The number of elements is also the size of the internal packed array.
|
||||
* There is no guarantee that the internal sparse array has the same size.
|
||||
* Usually the size of the internal sparse array is equal or greater than
|
||||
* the one of the internal packed array.
|
||||
*
|
||||
* @return Number of elements.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
|
||||
return packed.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a sparse set is empty.
|
||||
* @return True if the sparse set is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return packed.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Direct access to the internal packed array.
|
||||
* @return A pointer to the internal packed array.
|
||||
*/
|
||||
[[nodiscard]] pointer data() const ENTT_NOEXCEPT {
|
||||
return packed.data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
*
|
||||
* The returned iterator points to the first entity of the internal packed
|
||||
* array. If the sparse set is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the sparse set.
|
||||
*/
|
||||
[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
|
||||
const auto pos = static_cast<typename iterator::difference_type>(packed.size());
|
||||
return iterator{packed, pos};
|
||||
}
|
||||
|
||||
/*! @copydoc begin */
|
||||
[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
|
||||
return begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end.
|
||||
*
|
||||
* The returned iterator points to the element following the last entity in
|
||||
* a sparse set. Attempting to dereference the returned iterator results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @return An iterator to the element following the last entity of a sparse
|
||||
* set.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
|
||||
return iterator{packed, {}};
|
||||
}
|
||||
|
||||
/*! @copydoc end */
|
||||
[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
|
||||
return end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reverse iterator to the beginning.
|
||||
*
|
||||
* The returned iterator points to the first entity of the reversed internal
|
||||
* packed array. If the sparse set is empty, the returned iterator will be
|
||||
* equal to `rend()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the reversed internal packed
|
||||
* array.
|
||||
*/
|
||||
[[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT {
|
||||
return std::make_reverse_iterator(end());
|
||||
}
|
||||
|
||||
/*! @copydoc rbegin */
|
||||
[[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT {
|
||||
return rbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reverse iterator to the end.
|
||||
*
|
||||
* The returned iterator points to the element following the last entity in
|
||||
* the reversed sparse set. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the element following the last entity of the
|
||||
* reversed sparse set.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
|
||||
return std::make_reverse_iterator(begin());
|
||||
}
|
||||
|
||||
/*! @copydoc rend */
|
||||
[[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT {
|
||||
return rend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds an entity.
|
||||
* @param entt A valid identifier.
|
||||
* @return An iterator to the given entity if it's found, past the end
|
||||
* iterator otherwise.
|
||||
*/
|
||||
[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT {
|
||||
return contains(entt) ? --(end() - index(entt)) : end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a sparse set contains an entity.
|
||||
* @param entt A valid identifier.
|
||||
* @return True if the sparse set contains the entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT {
|
||||
const auto elem = sparse_ptr(entt);
|
||||
constexpr auto cap = entity_traits::to_entity(null);
|
||||
// testing versions permits to avoid accessing the packed array
|
||||
return elem && (((~cap & entity_traits::to_integral(entt)) ^ entity_traits::to_integral(*elem)) < cap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the contained version for an identifier.
|
||||
* @param entt A valid identifier.
|
||||
* @return The version for the given identifier if present, the tombstone
|
||||
* version otherwise.
|
||||
*/
|
||||
[[nodiscard]] version_type current(const entity_type entt) const {
|
||||
const auto elem = sparse_ptr(entt);
|
||||
constexpr auto fallback = entity_traits::to_version(tombstone);
|
||||
return elem ? entity_traits::to_version(*elem) : fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the position of an entity in a sparse set.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to get the position of an entity that doesn't belong to the
|
||||
* sparse set results in undefined behavior.
|
||||
*
|
||||
* @param entt A valid identifier.
|
||||
* @return The position of the entity in the sparse set.
|
||||
*/
|
||||
[[nodiscard]] size_type index(const entity_type entt) const ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(contains(entt), "Set does not contain entity");
|
||||
return static_cast<size_type>(entity_traits::to_entity(sparse_ref(entt)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the entity at specified location, with bounds checking.
|
||||
* @param pos The position for which to return the entity.
|
||||
* @return The entity at specified location if any, a null entity otherwise.
|
||||
*/
|
||||
[[nodiscard]] entity_type at(const size_type pos) const ENTT_NOEXCEPT {
|
||||
return pos < packed.size() ? packed[pos] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the entity at specified location, without bounds checking.
|
||||
* @param pos The position for which to return the entity.
|
||||
* @return The entity at specified location.
|
||||
*/
|
||||
[[nodiscard]] entity_type operator[](const size_type pos) const ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(pos < packed.size(), "Position is out of bounds");
|
||||
return packed[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the element assigned to an entity, if any.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an entity that doesn't belong to the sparse set results
|
||||
* in undefined behavior.
|
||||
*
|
||||
* @param entt A valid identifier.
|
||||
* @return An opaque pointer to the element assigned to the entity, if any.
|
||||
*/
|
||||
const void *get(const entity_type entt) const ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(contains(entt), "Set does not contain entity");
|
||||
return get_at(index(entt));
|
||||
}
|
||||
|
||||
/*! @copydoc get */
|
||||
void *get(const entity_type entt) ENTT_NOEXCEPT {
|
||||
return const_cast<void *>(std::as_const(*this).get(entt));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns an entity to a sparse set.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to assign an entity that already belongs to the sparse set
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @param entt A valid identifier.
|
||||
* @param value Optional opaque value to forward to mixins, if any.
|
||||
*/
|
||||
void emplace(const entity_type entt, const void *value = nullptr) {
|
||||
try_emplace(entt, value);
|
||||
ENTT_ASSERT(contains(entt), "Emplace did not take place");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns one or more entities to a sparse set.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to assign an entity that already belongs to the sparse set
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @tparam It Type of input iterator.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
*/
|
||||
template<typename It>
|
||||
void insert(It first, It last) {
|
||||
for(; first != last && free_list != null; ++first) {
|
||||
emplace(*first);
|
||||
}
|
||||
|
||||
reserve(packed.size() + std::distance(first, last));
|
||||
|
||||
for(; first != last; ++first) {
|
||||
emplace(*first);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases an entity from a sparse set.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to erase an entity that doesn't belong to the sparse set
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @param entt A valid identifier.
|
||||
*/
|
||||
void erase(const entity_type entt) {
|
||||
ENTT_ASSERT(contains(entt), "Set does not contain entity");
|
||||
(mode == deletion_policy::in_place) ? in_place_pop(entt) : swap_and_pop(entt);
|
||||
ENTT_ASSERT(!contains(entt), "Destruction did not take place");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases entities from a set.
|
||||
*
|
||||
* @sa erase
|
||||
*
|
||||
* @tparam It Type of input iterator.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
*/
|
||||
template<typename It>
|
||||
void erase(It first, It last) {
|
||||
for(; first != last; ++first) {
|
||||
erase(*first);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes an entity from a sparse set if it exists.
|
||||
* @param entt A valid identifier.
|
||||
* @return True if the entity is actually removed, false otherwise.
|
||||
*/
|
||||
bool remove(const entity_type entt) {
|
||||
return contains(entt) && (erase(entt), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes entities from a sparse set if they exist.
|
||||
* @tparam It Type of input iterator.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
* @return The number of entities actually removed.
|
||||
*/
|
||||
template<typename It>
|
||||
size_type remove(It first, It last) {
|
||||
size_type found{};
|
||||
|
||||
for(; first != last; ++first) {
|
||||
found += remove(*first);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/*! @brief Removes all tombstones from the packed array of a sparse set. */
|
||||
void compact() {
|
||||
size_type next = packed.size();
|
||||
for(; next && packed[next - 1u] == tombstone; --next) {}
|
||||
|
||||
for(auto *it = &free_list; *it != null && next; it = std::addressof(packed[entity_traits::to_entity(*it)])) {
|
||||
if(const size_type pos = entity_traits::to_entity(*it); pos < next) {
|
||||
--next;
|
||||
move_and_pop(next, pos);
|
||||
std::swap(packed[next], packed[pos]);
|
||||
const auto entity = static_cast<typename entity_traits::entity_type>(pos);
|
||||
sparse_ref(packed[pos]) = entity_traits::combine(entity, entity_traits::to_integral(packed[pos]));
|
||||
*it = entity_traits::combine(static_cast<typename entity_traits::entity_type>(next), entity_traits::reserved);
|
||||
for(; next && packed[next - 1u] == tombstone; --next) {}
|
||||
}
|
||||
}
|
||||
|
||||
free_list = tombstone;
|
||||
packed.resize(next);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copybrief swap_at
|
||||
*
|
||||
* For what it's worth, this function affects both the internal sparse array
|
||||
* and the internal packed array. Users should not care of that anyway.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to swap entities that don't belong to the sparse set results
|
||||
* in undefined behavior.
|
||||
*
|
||||
* @param lhs A valid identifier.
|
||||
* @param rhs A valid identifier.
|
||||
*/
|
||||
void swap_elements(const entity_type lhs, const entity_type rhs) {
|
||||
ENTT_ASSERT(contains(lhs) && contains(rhs), "Set does not contain entities");
|
||||
|
||||
auto &entt = sparse_ref(lhs);
|
||||
auto &other = sparse_ref(rhs);
|
||||
|
||||
const auto from = entity_traits::to_entity(entt);
|
||||
const auto to = entity_traits::to_entity(other);
|
||||
|
||||
// basic no-leak guarantee (with invalid state) if swapping throws
|
||||
swap_at(static_cast<size_type>(from), static_cast<size_type>(to));
|
||||
entt = entity_traits::combine(to, entity_traits::to_integral(packed[from]));
|
||||
other = entity_traits::combine(from, entity_traits::to_integral(packed[to]));
|
||||
std::swap(packed[from], packed[to]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort the first count elements according to the given comparison
|
||||
* function.
|
||||
*
|
||||
* The comparison function object must return `true` if the first element
|
||||
* is _less_ than the second one, `false` otherwise. The signature of the
|
||||
* comparison function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* bool(const Entity, const Entity);
|
||||
* @endcode
|
||||
*
|
||||
* Moreover, the comparison function object shall induce a
|
||||
* _strict weak ordering_ on the values.
|
||||
*
|
||||
* The sort function object must offer a member function template
|
||||
* `operator()` that accepts three arguments:
|
||||
*
|
||||
* * An iterator to the first element of the range to sort.
|
||||
* * An iterator past the last element of the range to sort.
|
||||
* * A comparison function to use to compare the elements.
|
||||
*
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Sort Type of sort function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function object.
|
||||
* @param length Number of elements to sort.
|
||||
* @param compare A valid comparison function object.
|
||||
* @param algo A valid sort function object.
|
||||
* @param args Arguments to forward to the sort function object, if any.
|
||||
*/
|
||||
template<typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) {
|
||||
ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements");
|
||||
ENTT_ASSERT(free_list == null, "Partial sorting with tombstones is not supported");
|
||||
|
||||
algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward<Args>(args)...);
|
||||
|
||||
for(size_type pos{}; pos < length; ++pos) {
|
||||
auto curr = pos;
|
||||
auto next = index(packed[curr]);
|
||||
|
||||
while(curr != next) {
|
||||
const auto idx = index(packed[next]);
|
||||
const auto entt = packed[curr];
|
||||
|
||||
swap_at(next, idx);
|
||||
const auto entity = static_cast<typename entity_traits::entity_type>(curr);
|
||||
sparse_ref(entt) = entity_traits::combine(entity, entity_traits::to_integral(packed[curr]));
|
||||
curr = std::exchange(next, idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort all elements according to the given comparison function.
|
||||
*
|
||||
* @sa sort_n
|
||||
*
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Sort Type of sort function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function object.
|
||||
* @param compare A valid comparison function object.
|
||||
* @param algo A valid sort function object.
|
||||
* @param args Arguments to forward to the sort function object, if any.
|
||||
*/
|
||||
template<typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
|
||||
compact();
|
||||
sort_n(packed.size(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort entities according to their order in another sparse set.
|
||||
*
|
||||
* Entities that are part of both the sparse sets are ordered internally
|
||||
* according to the order they have in `other`. All the other entities goes
|
||||
* to the end of the list and there are no guarantees on their order.<br/>
|
||||
* In other terms, this function can be used to impose the same order on two
|
||||
* sets by using one of them as a master and the other one as a slave.
|
||||
*
|
||||
* Iterating the sparse set with a couple of iterators returns elements in
|
||||
* the expected order after a call to `respect`. See `begin` and `end` for
|
||||
* more details.
|
||||
*
|
||||
* @param other The sparse sets that imposes the order of the entities.
|
||||
*/
|
||||
void respect(const basic_sparse_set &other) {
|
||||
compact();
|
||||
|
||||
const auto to = other.end();
|
||||
auto from = other.begin();
|
||||
|
||||
for(size_type pos = packed.size() - 1; pos && from != to; ++from) {
|
||||
if(contains(*from)) {
|
||||
if(*from != packed[pos]) {
|
||||
// basic no-leak guarantee (with invalid state) if swapping throws
|
||||
swap_elements(packed[pos], *from);
|
||||
}
|
||||
|
||||
--pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*! @brief Clears a sparse set. */
|
||||
void clear() {
|
||||
for(auto &&entity: *this) {
|
||||
// honor the modality and filter all tombstones
|
||||
remove(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returned value type, if any.
|
||||
* @return Returned value type, if any.
|
||||
*/
|
||||
const type_info &type() const ENTT_NOEXCEPT {
|
||||
return *info;
|
||||
}
|
||||
|
||||
/*! @brief Forwards variables to mixins, if any. */
|
||||
virtual void bind(any) ENTT_NOEXCEPT {}
|
||||
|
||||
private:
|
||||
sparse_container_type sparse;
|
||||
packed_container_type packed;
|
||||
const type_info *info;
|
||||
entity_type free_list;
|
||||
deletion_policy mode;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
1104
src/entt/entity/storage.hpp
Normal file
1104
src/entt/entity/storage.hpp
Normal file
File diff suppressed because it is too large
Load Diff
52
src/entt/entity/utility.hpp
Normal file
52
src/entt/entity/utility.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#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
|
||||
848
src/entt/entity/view.hpp
Normal file
848
src/entt/entity/view.hpp
Normal file
@@ -0,0 +1,848 @@
|
||||
#ifndef ENTT_ENTITY_VIEW_HPP
|
||||
#define ENTT_ENTITY_VIEW_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/iterator.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "component.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "sparse_set.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Type, std::size_t Component, std::size_t Exclude>
|
||||
class view_iterator final {
|
||||
[[nodiscard]] bool valid() const {
|
||||
return ((Component != 0u) || (*it != tombstone))
|
||||
&& std::apply([entt = *it](const auto *...curr) { return (curr->contains(entt) && ...); }, pools)
|
||||
&& std::apply([entt = *it](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter);
|
||||
}
|
||||
|
||||
public:
|
||||
using iterator_type = typename Type::iterator;
|
||||
using difference_type = typename std::iterator_traits<iterator_type>::difference_type;
|
||||
using value_type = typename std::iterator_traits<iterator_type>::value_type;
|
||||
using pointer = typename std::iterator_traits<iterator_type>::pointer;
|
||||
using reference = typename std::iterator_traits<iterator_type>::reference;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
view_iterator() ENTT_NOEXCEPT
|
||||
: it{},
|
||||
last{},
|
||||
pools{},
|
||||
filter{} {}
|
||||
|
||||
view_iterator(iterator_type curr, iterator_type to, std::array<const Type *, Component> all_of, std::array<const Type *, Exclude> none_of) ENTT_NOEXCEPT
|
||||
: it{curr},
|
||||
last{to},
|
||||
pools{all_of},
|
||||
filter{none_of} {
|
||||
if(it != last && !valid()) {
|
||||
++(*this);
|
||||
}
|
||||
}
|
||||
|
||||
view_iterator &operator++() ENTT_NOEXCEPT {
|
||||
while(++it != last && !valid()) {}
|
||||
return *this;
|
||||
}
|
||||
|
||||
view_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
view_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] pointer operator->() const {
|
||||
return &*it;
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs>
|
||||
friend bool operator==(const view_iterator<LhsType, LhsArgs...> &, const view_iterator<RhsType, RhsArgs...> &) ENTT_NOEXCEPT;
|
||||
|
||||
private:
|
||||
iterator_type it;
|
||||
iterator_type last;
|
||||
std::array<const Type *, Component> pools;
|
||||
std::array<const Type *, Exclude> filter;
|
||||
};
|
||||
|
||||
template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs>
|
||||
[[nodiscard]] bool operator==(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.it == rhs.it;
|
||||
}
|
||||
|
||||
template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs>
|
||||
[[nodiscard]] bool operator!=(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<typename It, typename... Storage>
|
||||
struct extended_view_iterator final {
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::declval<Storage>().get_as_tuple({})...));
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
extended_view_iterator() = default;
|
||||
|
||||
extended_view_iterator(It from, std::tuple<Storage *...> storage) ENTT_NOEXCEPT
|
||||
: it{from},
|
||||
pools{storage} {}
|
||||
|
||||
extended_view_iterator &operator++() ENTT_NOEXCEPT {
|
||||
return ++it, *this;
|
||||
}
|
||||
|
||||
extended_view_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
extended_view_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
|
||||
return std::apply([entt = *it](auto *...curr) { return std::tuple_cat(std::make_tuple(entt), curr->get_as_tuple(entt)...); }, pools);
|
||||
}
|
||||
|
||||
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
|
||||
return operator*();
|
||||
}
|
||||
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
friend bool operator==(const extended_view_iterator<Lhs...> &, const extended_view_iterator<Rhs...> &) ENTT_NOEXCEPT;
|
||||
|
||||
private:
|
||||
It it;
|
||||
std::tuple<Storage *...> pools;
|
||||
};
|
||||
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
[[nodiscard]] bool operator==(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.it == rhs.it;
|
||||
}
|
||||
|
||||
template<typename... Lhs, typename... Rhs>
|
||||
[[nodiscard]] bool operator!=(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief View implementation.
|
||||
*
|
||||
* Primary template isn't defined on purpose. All the specializations give a
|
||||
* compile-time error, but for a few reasonable cases.
|
||||
*/
|
||||
template<typename, typename, typename, typename>
|
||||
class basic_view;
|
||||
|
||||
/**
|
||||
* @brief Multi component view.
|
||||
*
|
||||
* Multi component views iterate over those entities that have at least all the
|
||||
* given components in their bags. During initialization, a multi component view
|
||||
* looks at the number of entities available for each component and uses the
|
||||
* smallest set in order to get a performance boost when iterate.
|
||||
*
|
||||
* @b Important
|
||||
*
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all other cases, modifying the pools iterated by the view in any way
|
||||
* invalidates all the iterators and using them results in undefined behavior.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Component Types of components iterated by the view.
|
||||
* @tparam Exclude Types of components used to filter the view.
|
||||
*/
|
||||
template<typename Entity, typename... Component, typename... Exclude>
|
||||
class basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>> {
|
||||
template<typename, typename, typename, typename>
|
||||
friend class basic_view;
|
||||
|
||||
template<typename Comp>
|
||||
using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>;
|
||||
|
||||
template<std::size_t... Index>
|
||||
[[nodiscard]] auto pools_to_array(std::index_sequence<Index...>) const ENTT_NOEXCEPT {
|
||||
std::size_t pos{};
|
||||
std::array<const base_type *, sizeof...(Component) - 1u> other{};
|
||||
(static_cast<void>(std::get<Index>(pools) == view ? void() : void(other[pos++] = std::get<Index>(pools))), ...);
|
||||
return other;
|
||||
}
|
||||
|
||||
template<std::size_t Comp, std::size_t Other, typename... Args>
|
||||
[[nodiscard]] auto dispatch_get(const std::tuple<Entity, Args...> &curr) const {
|
||||
if constexpr(Comp == Other) {
|
||||
return std::forward_as_tuple(std::get<Args>(curr)...);
|
||||
} else {
|
||||
return std::get<Other>(pools)->get_as_tuple(std::get<0>(curr));
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Comp, typename Func, std::size_t... Index>
|
||||
void each(Func func, std::index_sequence<Index...>) const {
|
||||
for(const auto curr: std::get<Comp>(pools)->each()) {
|
||||
const auto entt = std::get<0>(curr);
|
||||
|
||||
if(((sizeof...(Component) != 1u) || (entt != tombstone))
|
||||
&& ((Comp == Index || std::get<Index>(pools)->contains(entt)) && ...)
|
||||
&& std::apply([entt](const auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
|
||||
if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_view>().get({})))>) {
|
||||
std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get<Comp, Index>(curr)...));
|
||||
} else {
|
||||
std::apply(func, std::tuple_cat(dispatch_get<Comp, Index>(curr)...));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Func, std::size_t... Index>
|
||||
void pick_and_each(Func func, std::index_sequence<Index...> seq) const {
|
||||
((std::get<Index>(pools) == view ? each<Index>(std::move(func), seq) : void()), ...);
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Common type among all storage types. */
|
||||
using base_type = std::common_type_t<typename storage_type<Component>::base_type...>;
|
||||
/*! @brief Bidirectional iterator type. */
|
||||
using iterator = internal::view_iterator<base_type, sizeof...(Component) - 1u, sizeof...(Exclude)>;
|
||||
/*! @brief Iterable view type. */
|
||||
using iterable = iterable_adaptor<internal::extended_view_iterator<iterator, storage_type<Component>...>>;
|
||||
|
||||
/*! @brief Default constructor to use to create empty, invalid views. */
|
||||
basic_view() ENTT_NOEXCEPT
|
||||
: pools{},
|
||||
filter{},
|
||||
view{} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a multi-type view from a set of storage classes.
|
||||
* @param component The storage for the types to iterate.
|
||||
* @param epool The storage for the types used to filter the view.
|
||||
*/
|
||||
basic_view(storage_type<Component> &...component, const storage_type<Exclude> &...epool) ENTT_NOEXCEPT
|
||||
: pools{&component...},
|
||||
filter{&epool...},
|
||||
view{(std::min)({&static_cast<const base_type &>(component)...}, [](auto *lhs, auto *rhs) { return lhs->size() < rhs->size(); })} {}
|
||||
|
||||
/**
|
||||
* @brief Creates a new view driven by a given component in its iterations.
|
||||
* @tparam Comp Type of component used to drive the iteration.
|
||||
* @return A new view driven by the given component in its iterations.
|
||||
*/
|
||||
template<typename Comp>
|
||||
[[nodiscard]] basic_view use() const ENTT_NOEXCEPT {
|
||||
basic_view other{*this};
|
||||
other.view = std::get<storage_type<Comp> *>(pools);
|
||||
return other;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a new view driven by a given component in its iterations.
|
||||
* @tparam Comp Index of the component used to drive the iteration.
|
||||
* @return A new view driven by the given component in its iterations.
|
||||
*/
|
||||
template<std::size_t Comp>
|
||||
[[nodiscard]] basic_view use() const ENTT_NOEXCEPT {
|
||||
basic_view other{*this};
|
||||
other.view = std::get<Comp>(pools);
|
||||
return other;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the leading storage of a view.
|
||||
* @return The leading storage of the view.
|
||||
*/
|
||||
const base_type &handle() const ENTT_NOEXCEPT {
|
||||
return *view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the storage for a given component type.
|
||||
* @tparam Comp Type of component of which to return the storage.
|
||||
* @return The storage for the given component type.
|
||||
*/
|
||||
template<typename Comp>
|
||||
[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
|
||||
return *std::get<storage_type<Comp> *>(pools);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the storage for a given component type.
|
||||
* @tparam Comp Index of component of which to return the storage.
|
||||
* @return The storage for the given component type.
|
||||
*/
|
||||
template<std::size_t Comp>
|
||||
[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
|
||||
return *std::get<Comp>(pools);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Estimates the number of entities iterated by the view.
|
||||
* @return Estimated number of entities iterated by the view.
|
||||
*/
|
||||
[[nodiscard]] size_type size_hint() const ENTT_NOEXCEPT {
|
||||
return view->size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the view.
|
||||
*
|
||||
* The returned iterator points to the first entity of the view. If the view
|
||||
* is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the view.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const {
|
||||
return iterator{view->begin(), view->end(), pools_to_array(std::index_sequence_for<Component...>{}), filter};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the view.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the view. Attempting to dereference the returned iterator results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the view.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const {
|
||||
return iterator{view->end(), view->end(), pools_to_array(std::index_sequence_for<Component...>{}), filter};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the first entity of the view, if any.
|
||||
* @return The first entity of the view if one exists, the null entity
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] entity_type front() const {
|
||||
const auto it = begin();
|
||||
return it != end() ? *it : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the last entity of the view, if any.
|
||||
* @return The last entity of the view if one exists, the null entity
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] entity_type back() const {
|
||||
auto it = view->rbegin();
|
||||
for(const auto last = view->rend(); it != last && !contains(*it); ++it) {}
|
||||
return it == view->rend() ? null : *it;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds an entity.
|
||||
* @param entt A valid identifier.
|
||||
* @return An iterator to the given entity if it's found, past the end
|
||||
* iterator otherwise.
|
||||
*/
|
||||
[[nodiscard]] iterator find(const entity_type entt) const {
|
||||
return contains(entt) ? iterator{view->find(entt), view->end(), pools_to_array(std::index_sequence_for<Component...>{}), filter} : end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
* @param entt A valid identifier.
|
||||
* @return The components assigned to the given entity.
|
||||
*/
|
||||
[[nodiscard]] decltype(auto) operator[](const entity_type entt) const {
|
||||
return get<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a view is properly initialized.
|
||||
* @return True if the view is properly initialized, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return view != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a view contains an entity.
|
||||
* @param entt A valid identifier.
|
||||
* @return True if the view contains the given entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(const entity_type entt) const {
|
||||
return std::apply([entt](const auto *...curr) { return (curr->contains(entt) && ...); }, pools)
|
||||
&& std::apply([entt](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an entity that doesn't belong to the view results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @tparam Comp Types of components to get.
|
||||
* @param entt A valid identifier.
|
||||
* @return The components assigned to the entity.
|
||||
*/
|
||||
template<typename... Comp>
|
||||
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
|
||||
ENTT_ASSERT(contains(entt), "View does not contain entity");
|
||||
|
||||
if constexpr(sizeof...(Comp) == 0) {
|
||||
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools);
|
||||
} else if constexpr(sizeof...(Comp) == 1) {
|
||||
return (std::get<storage_type<Comp> *>(pools)->get(entt), ...);
|
||||
} else {
|
||||
return std::tuple_cat(std::get<storage_type<Comp> *>(pools)->get_as_tuple(entt)...);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an entity that doesn't belong to the view results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @tparam First Index of a component to get.
|
||||
* @tparam Other Indexes of other components to get.
|
||||
* @param entt A valid identifier.
|
||||
* @return The components assigned to the entity.
|
||||
*/
|
||||
template<std::size_t First, std::size_t... Other>
|
||||
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
|
||||
ENTT_ASSERT(contains(entt), "View does not contain entity");
|
||||
|
||||
if constexpr(sizeof...(Other) == 0) {
|
||||
return std::get<First>(pools)->get(entt);
|
||||
} else {
|
||||
return std::tuple_cat(std::get<First>(pools)->get_as_tuple(entt), std::get<Other>(pools)->get_as_tuple(entt)...);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and components and applies the given function
|
||||
* object to them.
|
||||
*
|
||||
* The function object is invoked for each entity. It is provided with the
|
||||
* entity itself and a set of references to non-empty components. The
|
||||
* _constness_ of the components is as requested.<br/>
|
||||
* The signature of the function must be equivalent to one of the following
|
||||
* forms:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type, Type &...);
|
||||
* void(Type &...);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
pick_and_each(std::move(func), std::index_sequence_for<Component...>{});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to use to _visit_ a view.
|
||||
*
|
||||
* The iterable object returns a tuple that contains the current entity and
|
||||
* a set of references to its non-empty components. The _constness_ of the
|
||||
* components is as requested.
|
||||
*
|
||||
* @return An iterable object to use to _visit_ the view.
|
||||
*/
|
||||
[[nodiscard]] iterable each() const ENTT_NOEXCEPT {
|
||||
return {internal::extended_view_iterator{begin(), pools}, internal::extended_view_iterator{end(), pools}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Combines two views in a _more specific_ one (friend function).
|
||||
* @tparam Get Component list of the view to combine with.
|
||||
* @tparam Excl Filter list of the view to combine with.
|
||||
* @param other The view to combine with.
|
||||
* @return A more specific view.
|
||||
*/
|
||||
template<typename... Get, typename... Excl>
|
||||
[[nodiscard]] auto operator|(const basic_view<Entity, get_t<Get...>, exclude_t<Excl...>> &other) const ENTT_NOEXCEPT {
|
||||
using view_type = basic_view<Entity, get_t<Component..., Get...>, exclude_t<Exclude..., Excl...>>;
|
||||
return std::make_from_tuple<view_type>(std::tuple_cat(
|
||||
std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, pools),
|
||||
std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, other.pools),
|
||||
std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast<const storage_type<Exclude> &>(*curr)...); }, filter),
|
||||
std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast<const storage_type<Excl> &>(*curr)...); }, other.filter)));
|
||||
}
|
||||
|
||||
private:
|
||||
std::tuple<storage_type<Component> *...> pools;
|
||||
std::array<const base_type *, sizeof...(Exclude)> filter;
|
||||
const base_type *view;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Single component view specialization.
|
||||
*
|
||||
* Single component views are specialized in order to get a boost in terms of
|
||||
* performance. This kind of views can access the underlying data structure
|
||||
* directly and avoid superfluous checks.
|
||||
*
|
||||
* @b Important
|
||||
*
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New instances of the given component are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, the given
|
||||
* component is removed from the entity to which the iterator points).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all other cases, modifying the pool iterated by the view in any way
|
||||
* invalidates all the iterators and using them results in undefined behavior.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Component Type of component iterated by the view.
|
||||
*/
|
||||
template<typename Entity, typename Component>
|
||||
class basic_view<Entity, get_t<Component>, exclude_t<>, std::void_t<std::enable_if_t<!component_traits<std::remove_const_t<Component>>::in_place_delete>>> {
|
||||
template<typename, typename, typename, typename>
|
||||
friend class basic_view;
|
||||
|
||||
using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Component>>::storage_type, Component>;
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Common type among all storage types. */
|
||||
using base_type = typename storage_type::base_type;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = typename base_type::iterator;
|
||||
/*! @brief Reversed iterator type. */
|
||||
using reverse_iterator = typename base_type::reverse_iterator;
|
||||
/*! @brief Iterable view type. */
|
||||
using iterable = decltype(std::declval<storage_type>().each());
|
||||
|
||||
/*! @brief Default constructor to use to create empty, invalid views. */
|
||||
basic_view() ENTT_NOEXCEPT
|
||||
: pools{},
|
||||
filter{},
|
||||
view{} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a single-type view from a storage class.
|
||||
* @param ref The storage for the type to iterate.
|
||||
*/
|
||||
basic_view(storage_type &ref) ENTT_NOEXCEPT
|
||||
: pools{&ref},
|
||||
filter{},
|
||||
view{&ref} {}
|
||||
|
||||
/**
|
||||
* @brief Returns the leading storage of a view.
|
||||
* @return The leading storage of the view.
|
||||
*/
|
||||
const base_type &handle() const ENTT_NOEXCEPT {
|
||||
return *view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the storage for a given component type.
|
||||
* @tparam Comp Type of component of which to return the storage.
|
||||
* @return The storage for the given component type.
|
||||
*/
|
||||
template<typename... Comp>
|
||||
[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
|
||||
static_assert((std::is_same_v<Comp, Component> && ...), "Invalid component type");
|
||||
return *std::get<0>(pools);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the storage for a given component type.
|
||||
* @tparam Comp Index of component of which to return the storage.
|
||||
* @return The storage for the given component type.
|
||||
*/
|
||||
template<std::size_t Comp>
|
||||
[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT {
|
||||
return *std::get<Comp>(pools);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of entities that have the given component.
|
||||
* @return Number of entities that have the given component.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
|
||||
return view->size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a view is empty.
|
||||
* @return True if the view is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return view->empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the view.
|
||||
*
|
||||
* The returned iterator points to the first entity of the view. If the view
|
||||
* is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the view.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
|
||||
return view->begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the view.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the view. Attempting to dereference the returned iterator results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the view.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
|
||||
return view->end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the reversed view.
|
||||
*
|
||||
* The returned iterator points to the first entity of the reversed view. If
|
||||
* the view is empty, the returned iterator will be equal to `rend()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the reversed view.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
|
||||
return view->rbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the reversed
|
||||
* view.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the reversed view. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* reversed view.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
|
||||
return view->rend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the first entity of the view, if any.
|
||||
* @return The first entity of the view if one exists, the null entity
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] entity_type front() const {
|
||||
return empty() ? null : *begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the last entity of the view, if any.
|
||||
* @return The last entity of the view if one exists, the null entity
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] entity_type back() const {
|
||||
return empty() ? null : *rbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds an entity.
|
||||
* @param entt A valid identifier.
|
||||
* @return An iterator to the given entity if it's found, past the end
|
||||
* iterator otherwise.
|
||||
*/
|
||||
[[nodiscard]] iterator find(const entity_type entt) const {
|
||||
return contains(entt) ? view->find(entt) : end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the identifier that occupies the given position.
|
||||
* @param pos Position of the element to return.
|
||||
* @return The identifier that occupies the given position.
|
||||
*/
|
||||
[[nodiscard]] entity_type operator[](const size_type pos) const {
|
||||
return begin()[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the component assigned to the given entity.
|
||||
* @param entt A valid identifier.
|
||||
* @return The component assigned to the given entity.
|
||||
*/
|
||||
[[nodiscard]] decltype(auto) operator[](const entity_type entt) const {
|
||||
return get<Component>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a view is properly initialized.
|
||||
* @return True if the view is properly initialized, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return view != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a view contains an entity.
|
||||
* @param entt A valid identifier.
|
||||
* @return True if the view contains the given entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(const entity_type entt) const {
|
||||
return view->contains(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the component assigned to the given entity.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an entity that doesn't belong to the view results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @tparam Comp Type or index of the component to get.
|
||||
* @param entt A valid identifier.
|
||||
* @return The component assigned to the entity.
|
||||
*/
|
||||
template<typename... Comp>
|
||||
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
|
||||
ENTT_ASSERT(contains(entt), "View does not contain entity");
|
||||
|
||||
if constexpr(sizeof...(Comp) == 0) {
|
||||
return std::get<0>(pools)->get_as_tuple(entt);
|
||||
} else {
|
||||
static_assert(std::is_same_v<Comp..., Component>, "Invalid component type");
|
||||
return std::get<0>(pools)->get(entt);
|
||||
}
|
||||
}
|
||||
|
||||
/*! @copydoc get */
|
||||
template<std::size_t Comp>
|
||||
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
|
||||
ENTT_ASSERT(contains(entt), "View does not contain entity");
|
||||
return std::get<0>(pools)->get(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and components and applies the given function
|
||||
* object to them.
|
||||
*
|
||||
* The function object is invoked for each entity. It is provided with the
|
||||
* entity itself and a reference to the component if it's a non-empty one.
|
||||
* The _constness_ of the component is as requested.<br/>
|
||||
* The signature of the function must be equivalent to one of the following
|
||||
* forms:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type, Component &);
|
||||
* void(Component &);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
if constexpr(ignore_as_empty_v<std::remove_const_t<Component>>) {
|
||||
if constexpr(std::is_invocable_v<Func>) {
|
||||
for(size_type pos{}, last = size(); pos < last; ++pos) {
|
||||
func();
|
||||
}
|
||||
} else {
|
||||
for(auto entity: *view) {
|
||||
func(entity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if constexpr(is_applicable_v<Func, decltype(*each().begin())>) {
|
||||
for(const auto pack: each()) {
|
||||
std::apply(func, pack);
|
||||
}
|
||||
} else {
|
||||
for(auto &&component: *std::get<0>(pools)) {
|
||||
func(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to use to _visit_ a view.
|
||||
*
|
||||
* The iterable object returns a tuple that contains the current entity and
|
||||
* a reference to its component if it's a non-empty one. The _constness_ of
|
||||
* the component is as requested.
|
||||
*
|
||||
* @return An iterable object to use to _visit_ the view.
|
||||
*/
|
||||
[[nodiscard]] iterable each() const ENTT_NOEXCEPT {
|
||||
return std::get<0>(pools)->each();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Combines two views in a _more specific_ one (friend function).
|
||||
* @tparam Get Component list of the view to combine with.
|
||||
* @tparam Excl Filter list of the view to combine with.
|
||||
* @param other The view to combine with.
|
||||
* @return A more specific view.
|
||||
*/
|
||||
template<typename... Get, typename... Excl>
|
||||
[[nodiscard]] auto operator|(const basic_view<Entity, get_t<Get...>, exclude_t<Excl...>> &other) const ENTT_NOEXCEPT {
|
||||
using view_type = basic_view<Entity, get_t<Component, Get...>, exclude_t<Excl...>>;
|
||||
return std::make_from_tuple<view_type>(std::tuple_cat(
|
||||
std::forward_as_tuple(*std::get<0>(pools)),
|
||||
std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, other.pools),
|
||||
std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast<const typename view_type::template storage_type<Excl> &>(*curr)...); }, other.filter)));
|
||||
}
|
||||
|
||||
private:
|
||||
std::tuple<storage_type *> pools;
|
||||
std::array<const base_type *, 0u> filter;
|
||||
const base_type *view;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Storage Type of storage classes used to create the view.
|
||||
* @param storage The storage for the types to iterate.
|
||||
*/
|
||||
template<typename... Storage>
|
||||
basic_view(Storage &...storage) -> basic_view<std::common_type_t<typename Storage::entity_type...>, get_t<constness_as_t<typename Storage::value_type, Storage>...>, exclude_t<>>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
57
src/entt/entt.hpp
Normal file
57
src/entt/entt.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "config/version.h"
|
||||
#include "container/dense_hash_map.hpp"
|
||||
#include "container/dense_hash_set.hpp"
|
||||
#include "core/algorithm.hpp"
|
||||
#include "core/any.hpp"
|
||||
#include "core/attribute.h"
|
||||
#include "core/compressed_pair.hpp"
|
||||
#include "core/enum.hpp"
|
||||
#include "core/family.hpp"
|
||||
#include "core/hashed_string.hpp"
|
||||
#include "core/ident.hpp"
|
||||
#include "core/iterator.hpp"
|
||||
#include "core/memory.hpp"
|
||||
#include "core/monostate.hpp"
|
||||
#include "core/tuple.hpp"
|
||||
#include "core/type_info.hpp"
|
||||
#include "core/type_traits.hpp"
|
||||
#include "core/utility.hpp"
|
||||
#include "entity/component.hpp"
|
||||
#include "entity/entity.hpp"
|
||||
#include "entity/group.hpp"
|
||||
#include "entity/handle.hpp"
|
||||
#include "entity/helper.hpp"
|
||||
#include "entity/observer.hpp"
|
||||
#include "entity/organizer.hpp"
|
||||
#include "entity/registry.hpp"
|
||||
#include "entity/runtime_view.hpp"
|
||||
#include "entity/snapshot.hpp"
|
||||
#include "entity/sparse_set.hpp"
|
||||
#include "entity/storage.hpp"
|
||||
#include "entity/utility.hpp"
|
||||
#include "entity/view.hpp"
|
||||
#include "locator/locator.hpp"
|
||||
#include "meta/adl_pointer.hpp"
|
||||
#include "meta/container.hpp"
|
||||
#include "meta/ctx.hpp"
|
||||
#include "meta/factory.hpp"
|
||||
#include "meta/meta.hpp"
|
||||
#include "meta/node.hpp"
|
||||
#include "meta/pointer.hpp"
|
||||
#include "meta/policy.hpp"
|
||||
#include "meta/range.hpp"
|
||||
#include "meta/resolve.hpp"
|
||||
#include "meta/template.hpp"
|
||||
#include "meta/type_traits.hpp"
|
||||
#include "meta/utility.hpp"
|
||||
#include "platform/android-ndk-r17.hpp"
|
||||
#include "poly/poly.hpp"
|
||||
#include "process/process.hpp"
|
||||
#include "process/scheduler.hpp"
|
||||
#include "resource/cache.hpp"
|
||||
#include "resource/handle.hpp"
|
||||
#include "resource/loader.hpp"
|
||||
#include "signal/delegate.hpp"
|
||||
#include "signal/dispatcher.hpp"
|
||||
#include "signal/emitter.hpp"
|
||||
#include "signal/sigh.hpp"
|
||||
7
src/entt/fwd.hpp
Normal file
7
src/entt/fwd.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "container/fwd.hpp"
|
||||
#include "core/fwd.hpp"
|
||||
#include "entity/fwd.hpp"
|
||||
#include "meta/fwd.hpp"
|
||||
#include "poly/fwd.hpp"
|
||||
#include "resource/fwd.hpp"
|
||||
#include "signal/fwd.hpp"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user