Compare commits
834 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
18
.github/FUNDING.yml
vendored
18
.github/FUNDING.yml
vendored
@@ -1,12 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # [skypjack] available only for those enrolled in the github sponsor program
|
||||
patreon: skypjack
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
github: skypjack
|
||||
patreon:
|
||||
open_collective:
|
||||
ko_fi:
|
||||
tidelift:
|
||||
community_bridge:
|
||||
liberapay:
|
||||
issuehunt:
|
||||
otechie:
|
||||
custom: https://www.paypal.me/skypjack
|
||||
|
||||
79
.github/workflows/build.yml
vendored
Normal file
79
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
name: build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
|
||||
linux:
|
||||
timeout-minutes: 10
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
compiler: [g++, clang++]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
env:
|
||||
CXX: ${{ matrix.compiler }}
|
||||
run: |
|
||||
cmake -DBUILD_LIB=ON -DBUILD_EXAMPLE=ON ..
|
||||
make -j4
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 5 -C Debug -j4
|
||||
|
||||
windows:
|
||||
timeout-minutes: 10
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest, windows-2016]
|
||||
toolset: [clang-cl, default, v141]
|
||||
include:
|
||||
- toolset: clang-cl
|
||||
toolset_option: -T"ClangCl"
|
||||
- toolset: v141
|
||||
toolset_option: -T"v141"
|
||||
exclude:
|
||||
- os: windows-2016
|
||||
toolset: clang-cl
|
||||
- os: windows-2016
|
||||
toolset: v141
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
cmake -DBUILD_LIB=ON -DBUILD_EXAMPLE=ON ${{ matrix.toolset_option }} ..
|
||||
cmake --build . -j 4
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 5 -C Debug -j4
|
||||
|
||||
macos:
|
||||
timeout-minutes: 10
|
||||
runs-on: macOS-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
cmake -DBUILD_LIB=ON -DBUILD_EXAMPLE=ON ..
|
||||
make -j4
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 5 -C Debug -j4
|
||||
33
.github/workflows/coverage.yml
vendored
Normal file
33
.github/workflows/coverage.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
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: "-O0 --coverage -fno-inline -fno-inline-small-functions -fno-default-inline"
|
||||
CXX: g++
|
||||
run: |
|
||||
cmake -DBUILD_LIB=ON -DBUILD_EXAMPLE=ON ..
|
||||
make -j4
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 5 -C Debug -j4
|
||||
- name: Upload coverage to Codecov
|
||||
working-directory: build
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
run: |
|
||||
wget https://codecov.io/bash -O codecov
|
||||
chmod +x codecov
|
||||
./codecov -t $CODECOV_TOKEN -B $GITHUB_REF -s .
|
||||
40
.github/workflows/deploy.yml
vendored
Normal file
40
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
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
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,8 +1,12 @@
|
||||
*.user
|
||||
# Conan
|
||||
conan/test_package/build
|
||||
|
||||
# IDEs
|
||||
*.user
|
||||
.idea
|
||||
.vscode
|
||||
.vs
|
||||
CMakeSettings.json
|
||||
|
||||
# Bazel
|
||||
/bazel-*
|
||||
|
||||
97
.travis.yml
97
.travis.yml
@@ -1,97 +0,0 @@
|
||||
language: cpp
|
||||
dist: trusty
|
||||
sudo: false
|
||||
|
||||
env:
|
||||
global:
|
||||
- CONAN_USERNAME="skypjack"
|
||||
- CONAN_PACKAGE_NAME="entt"
|
||||
- CONAN_HEADER_ONLY="True"
|
||||
- NON_CONAN_DEPLOYMENT="True"
|
||||
|
||||
conan-buildsteps: &conan-buildsteps
|
||||
before_install:
|
||||
# use this step if you desire to manipulate CONAN variables programmatically
|
||||
- NON_CONAN_DEPLOYMENT="False"
|
||||
install:
|
||||
- chmod +x ./conan/ci/install.sh
|
||||
- ./conan/ci/install.sh
|
||||
script:
|
||||
- chmod +x ./conan/ci/build.sh
|
||||
- ./conan/ci/build.sh
|
||||
# the following are dummies to overwrite default build steps
|
||||
before_script:
|
||||
- true
|
||||
after_success:
|
||||
- true
|
||||
if: tag IS present
|
||||
conan-linux: &conan-linux
|
||||
os: linux
|
||||
sudo: required
|
||||
language: python
|
||||
python: "3.6"
|
||||
services:
|
||||
- docker
|
||||
<<: *conan-buildsteps
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-7']
|
||||
env: COMPILER=g++-7
|
||||
- os: linux
|
||||
compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-6.0']
|
||||
packages: ['clang-6.0', 'g++-7']
|
||||
env: COMPILER=clang++-6.0
|
||||
- os: osx
|
||||
osx_image: xcode10
|
||||
compiler: clang
|
||||
env: COMPILER=clang++
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-7']
|
||||
env:
|
||||
- COMPILER=g++-7
|
||||
- CXXFLAGS="-O0 --coverage -fno-inline -fno-inline-small-functions -fno-default-inline"
|
||||
before_script:
|
||||
- pip install --user cpp-coveralls
|
||||
after_success:
|
||||
- coveralls --gcov gcov-7 --gcov-options '\-lp' --root ${TRAVIS_BUILD_DIR} --build-root ${TRAVIS_BUILD_DIR}/build --extension cpp --extension hpp --exclude deps --include src
|
||||
# Conan testing and uploading
|
||||
- <<: *conan-linux
|
||||
env: CONAN_GCC_VERSIONS=8 CONAN_DOCKER_IMAGE=conanio/gcc8
|
||||
|
||||
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 -DBUILD_TESTING=ON -DBUILD_LIB=ON .. && make -j4
|
||||
- CTEST_OUTPUT_ON_FAILURE=1 ctest --timeout 5 -C Debug -j4
|
||||
|
||||
deploy:
|
||||
provider: script
|
||||
script: scripts/update_packages.sh $TRAVIS_TAG
|
||||
on:
|
||||
tags: true
|
||||
condition: “$NON_CONAN_DEPLOYMENT = “True”
|
||||
13
AUTHORS
13
AUTHORS
@@ -4,33 +4,46 @@ skypjack
|
||||
|
||||
# Contributors
|
||||
|
||||
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
|
||||
Lawrencemm
|
||||
markand
|
||||
mhammerc
|
||||
Milerius
|
||||
morbo84
|
||||
m-waka
|
||||
NixAJ
|
||||
Oortonaut
|
||||
Paolo-Oliverio
|
||||
pgruenbacher
|
||||
prowolf
|
||||
stefanofiorentino
|
||||
suVrik
|
||||
szunhammer
|
||||
The5-1
|
||||
vblanco20-1
|
||||
willtunnels
|
||||
WizardIke
|
||||
w1th0utnam3
|
||||
xissburg
|
||||
zaucy
|
||||
|
||||
10
BUILD.bazel
10
BUILD.bazel
@@ -1,9 +1,15 @@
|
||||
load("//bazel:copts.bzl", "entt_copts")
|
||||
_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 = entt_copts,
|
||||
copts = select({
|
||||
"@bazel_tools//src/conditions:windows": _msvc_copts,
|
||||
"@bazel_tools//src/conditions:windows_msvc": _msvc_copts,
|
||||
"@bazel_tools//src/conditions:windows_msys": _msvc_copts,
|
||||
"//conditions:default": _gcc_copts,
|
||||
}),
|
||||
)
|
||||
|
||||
172
CMakeLists.txt
172
CMakeLists.txt
@@ -2,7 +2,7 @@
|
||||
# EnTT
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.7.2)
|
||||
cmake_minimum_required(VERSION 3.12.4)
|
||||
|
||||
#
|
||||
# Building in-tree is not allowed (we take care of your craziness).
|
||||
@@ -12,37 +12,44 @@ 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 3.1.0)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
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 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-2019 ${PROJECT_AUTHOR} <${PROJECT_AUTHOR_EMAIL}>")
|
||||
message("*")
|
||||
message(VERBOSE "*")
|
||||
message(VERBOSE "* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
|
||||
message(VERBOSE "* Copyright (c) 2017-2020 Michele Caini <michele.caini@gmail.com>")
|
||||
message(VERBOSE "*")
|
||||
|
||||
option(USE_LIBCPP "Use libc++ by adding -stdlib=libc++ flag if availbale." ON)
|
||||
option(USE_ASAN "Use address sanitizer by adding -fsanitize=address -fno-omit-frame-pointer flags" OFF)
|
||||
option(USE_COMPILE_OPTIONS "Use compile options from EnTT." ON)
|
||||
|
||||
#
|
||||
# Compiler stuff
|
||||
#
|
||||
|
||||
if(NOT MSVC AND USE_LIBCPP)
|
||||
if(NOT WIN32 AND USE_LIBCPP)
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(CMakePushCheckState)
|
||||
|
||||
@@ -52,11 +59,11 @@ if(NOT MSVC AND USE_LIBCPP)
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include<type_traits>
|
||||
int main() { return std::is_same_v<int, int> ? 0 : 1; }
|
||||
int main() { return std::is_same_v<int, char>; }
|
||||
" HAS_LIBCPP)
|
||||
|
||||
if(NOT HAS_LIBCPP)
|
||||
message(WARNING "The option USE_LIBCPP is set (by default) but libc++ is not available. The flag will not be added to the target.")
|
||||
message(VERBOSE "The option USE_LIBCPP is set (by default) but libc++ is not available. The flag will not be added to the target.")
|
||||
endif()
|
||||
|
||||
cmake_pop_check_state()
|
||||
@@ -66,36 +73,21 @@ endif()
|
||||
# Add EnTT target
|
||||
#
|
||||
|
||||
add_library(EnTT INTERFACE)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
configure_file(${EnTT_SOURCE_DIR}/cmake/in/version.h.in ${EnTT_SOURCE_DIR}/src/entt/config/version.h @ONLY)
|
||||
add_library(EnTT INTERFACE)
|
||||
add_library(EnTT::EnTT ALIAS EnTT)
|
||||
|
||||
target_include_directories(
|
||||
EnTT INTERFACE
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
)
|
||||
|
||||
target_compile_definitions(
|
||||
EnTT
|
||||
INTERFACE $<$<AND:$<CONFIG:Debug>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:DEBUG>
|
||||
INTERFACE $<$<AND:$<CONFIG:Release>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:RELEASE>
|
||||
INTERFACE
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
)
|
||||
|
||||
if(USE_ASAN)
|
||||
target_compile_options(EnTT INTERFACE $<$<AND:$<CONFIG:Debug>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:-fsanitize=address -fno-omit-frame-pointer>)
|
||||
target_link_libraries(EnTT INTERFACE $<$<AND:$<CONFIG:Debug>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:-fsanitize=address -fno-omit-frame-pointer>)
|
||||
endif()
|
||||
|
||||
if(USE_COMPILE_OPTIONS)
|
||||
target_compile_options(
|
||||
EnTT
|
||||
INTERFACE $<$<AND:$<CONFIG:Debug>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:-O0 -g>
|
||||
# it seems that -O3 ruins a bit the performance when using clang ...
|
||||
INTERFACE $<$<AND:$<CONFIG:Release>,$<CXX_COMPILER_ID:Clang>>:-O2>
|
||||
# ... on the other side, GCC is incredibly comfortable with it.
|
||||
INTERFACE $<$<AND:$<CONFIG:Release>,$<CXX_COMPILER_ID:GNU>>:-O3>
|
||||
)
|
||||
target_compile_options(EnTT INTERFACE $<$<CONFIG:Debug>:-fsanitize=address -fno-omit-frame-pointer>)
|
||||
target_link_libraries(EnTT INTERFACE $<$<CONFIG:Debug>:-fsanitize=address -fno-omit-frame-pointer>)
|
||||
endif()
|
||||
|
||||
if(HAS_LIBCPP)
|
||||
@@ -108,88 +100,62 @@ target_compile_features(EnTT INTERFACE cxx_std_17)
|
||||
# Install EnTT
|
||||
#
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
||||
set(CUSTOM_INSTALL_CONFIGDIR cmake)
|
||||
else()
|
||||
set(CUSTOM_INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/entt)
|
||||
endif()
|
||||
|
||||
install(DIRECTORY src/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
install(TARGETS EnTT EXPORT EnTTTargets)
|
||||
|
||||
export(EXPORT EnTTTargets FILE ${EnTT_BINARY_DIR}/EnTTTargets.cmake)
|
||||
|
||||
install(
|
||||
EXPORT EnTTTargets
|
||||
FILE EnTTTargets.cmake
|
||||
DESTINATION ${CUSTOM_INSTALL_CONFIGDIR}
|
||||
)
|
||||
|
||||
#
|
||||
# Build tree package config file
|
||||
#
|
||||
|
||||
configure_file(cmake/in/EnTTBuildConfig.cmake.in EnTTConfig.cmake @ONLY)
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
#
|
||||
# Install tree package config file
|
||||
#
|
||||
|
||||
configure_package_config_file(
|
||||
cmake/in/EnTTConfig.cmake.in
|
||||
${CUSTOM_INSTALL_CONFIGDIR}/EnTTConfig.cmake
|
||||
INSTALL_DESTINATION ${CUSTOM_INSTALL_CONFIGDIR}
|
||||
PATH_VARS CMAKE_INSTALL_INCLUDEDIR
|
||||
install(
|
||||
TARGETS EnTT
|
||||
EXPORT EnTTTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
|
||||
write_basic_package_version_file(
|
||||
${EnTT_BINARY_DIR}/EnTTConfigVersion.cmake
|
||||
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
|
||||
${EnTT_BINARY_DIR}/${CUSTOM_INSTALL_CONFIGDIR}/EnTTConfig.cmake
|
||||
${EnTT_BINARY_DIR}/EnTTConfigVersion.cmake
|
||||
DESTINATION ${CUSTOM_INSTALL_CONFIGDIR}
|
||||
${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(BUILD_TESTING "Enable testing with ctest." OFF)
|
||||
include(CTest)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
option(FIND_GTEST_PACKAGE "Enable finding gtest package." OFF)
|
||||
|
||||
if(FIND_GTEST_PACKAGE)
|
||||
find_package(GTest REQUIRED)
|
||||
else()
|
||||
# gtest, gtest_main, gmock and gmock_main targets are available from now on
|
||||
set(GOOGLETEST_DEPS_DIR ${EnTT_SOURCE_DIR}/deps/googletest)
|
||||
configure_file(${EnTT_SOURCE_DIR}/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)
|
||||
target_compile_features(gmock_main PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_FEATURES>)
|
||||
target_compile_features(gmock PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_FEATURES>)
|
||||
add_library(GTest::Main ALIAS gtest_main)
|
||||
endif()
|
||||
|
||||
option(BUILD_BENCHMARK "Build benchmark." OFF)
|
||||
option(BUILD_LIB "Build lib example." OFF)
|
||||
option(BUILD_MOD "Build mod example." OFF)
|
||||
option(BUILD_SNAPSHOT "Build snapshot example." OFF)
|
||||
option(BUILD_EXAMPLE "Build examples." OFF)
|
||||
option(BUILD_LIB "Build lib tests." OFF)
|
||||
option(BUILD_SNAPSHOT "Build snapshot test with Cereal." OFF)
|
||||
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
@@ -214,13 +180,15 @@ endif()
|
||||
#
|
||||
|
||||
add_custom_target(
|
||||
entt_aob
|
||||
aob
|
||||
SOURCES
|
||||
appveyor.yml
|
||||
.github/workflows/build.yml
|
||||
.github/workflows/coverage.yml
|
||||
.github/workflows/deploy.yml
|
||||
.github/FUNDING.yml
|
||||
AUTHORS
|
||||
CONTRIBUTING.md
|
||||
LICENSE
|
||||
README.md
|
||||
TODO
|
||||
.travis.yml
|
||||
)
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2019 Michele Caini
|
||||
Copyright (c) 2017-2020 Michele Caini
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
211
README.md
211
README.md
@@ -3,13 +3,14 @@
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
[](http://badge.fury.io/gh/skypjack%2Fentt)
|
||||
[](https://github.com/skypjack/entt)
|
||||
[](https://travis-ci.org/skypjack/entt)
|
||||
[](https://ci.appveyor.com/project/skypjack/entt)
|
||||
[](https://coveralls.io/github/skypjack/entt?branch=master)
|
||||
[](https://github.com/skypjack/entt/releases)
|
||||
[](https://github.com/skypjack/entt/actions)
|
||||
[](https://codecov.io/gh/skypjack/entt)
|
||||
[](https://godbolt.org/z/cOUcm1)
|
||||
[](http://entt.docsforge.com/)
|
||||
[](https://gitter.im/skypjack/entt)
|
||||
[](https://www.paypal.me/skypjack)
|
||||
[](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++**, mainly known for its innovative
|
||||
@@ -17,14 +18,17 @@ much more written in **modern C++**, mainly known for its innovative
|
||||
[Among others](https://github.com/skypjack/entt/wiki/EnTT-in-Action), it's used
|
||||
in [**Minecraft**](https://minecraft.net/en-us/attribution/) by Mojang and the
|
||||
[**ArcGIS Runtime SDKs**](https://developers.arcgis.com/arcgis-runtime/) by
|
||||
Esri. Open an issue or submit a PR if you don't see your project in the list!
|
||||
Esri.<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 meet other users
|
||||
like you. The more we are, the better for everyone.
|
||||
Join the [gitter channel](https://gitter.im/skypjack/entt) or the
|
||||
[discord server](https://discord.gg/5BjPWBd) and meet other users like you. The
|
||||
more we are, the better for everyone.
|
||||
|
||||
Wondering why your **debug build** is so slow on Windows or how to represent a
|
||||
**hierarchy** with components?<br/>
|
||||
@@ -34,12 +38,11 @@ Check out the
|
||||
your answers may already be there.
|
||||
|
||||
If you use `EnTT` and you want to say thanks or support the project, please
|
||||
**consider becoming a patron**:
|
||||
|
||||
[](https://www.patreon.com/bePatron?c=1772573)
|
||||
|
||||
[Many thanks](https://skypjack.github.io/patreon/) to those who supported me and
|
||||
still support me today.
|
||||
**consider becoming a
|
||||
[sponsor](https://github.com/users/skypjack/sponsorship)**.<br/>
|
||||
You can help me make the difference.
|
||||
[Many thanks](https://skypjack.github.io/sponsorship/) to those who supported me
|
||||
and still support me today.
|
||||
|
||||
# Table of Contents
|
||||
|
||||
@@ -57,9 +60,6 @@ still support me today.
|
||||
* [Contributors](#contributors)
|
||||
* [License](#license)
|
||||
* [Support](#support)
|
||||
* [Patreon](#patreon)
|
||||
* [Donation](#donation)
|
||||
* [Hire me](#hire-me)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
@@ -102,10 +102,10 @@ Here is a brief, yet incomplete list of what it offers today:
|
||||
Consider this list a work in progress as well as the project. The whole API is
|
||||
fully documented in-code for those who are brave enough to read it.
|
||||
|
||||
Currently, `EnTT` is tested on Linux, Microsoft Windows and OSX. It has proven
|
||||
to work also on both Android and iOS.<br/>
|
||||
Most likely it won't be problematic on other systems as well, but it hasn't been
|
||||
sufficiently tested so far.
|
||||
It is also known that `EnTT` (version 3) 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
|
||||
|
||||
@@ -155,8 +155,8 @@ int main() {
|
||||
|
||||
for(auto i = 0; i < 10; ++i) {
|
||||
auto entity = registry.create();
|
||||
registry.assign<position>(entity, i * 1.f, i * 1.f);
|
||||
if(i % 2 == 0) { registry.assign<velocity>(entity, i * .1f, i * .1f); }
|
||||
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);
|
||||
@@ -182,48 +182,30 @@ amazing set of features. And even more, of course.
|
||||
|
||||
## Performance
|
||||
|
||||
As it stands right now, `EnTT` is just fast enough for my requirements when
|
||||
compared to my first choice (it was already amazingly fast actually).<br/>
|
||||
Below is a comparison between the two (both of them compiled with GCC 7.3.0 on a
|
||||
Dell XPS 13 from mid 2014):
|
||||
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.
|
||||
|
||||
| Benchmark | EntityX (compile-time) | EnTT |
|
||||
|-----------|-------------|-------------|
|
||||
| Create 1M entities | 0.0147s | **0.0046s** |
|
||||
| Destroy 1M entities | 0.0053s | **0.0045s** |
|
||||
| 1M entities, one component | 0.0012s | **1.9e-07s** |
|
||||
| 1M entities, two components | 0.0012s | **3.8e-07s** |
|
||||
| 1M entities, two components<br/>Half of the entities have all the components | 0.0009s | **3.8e-07s** |
|
||||
| 1M entities, two components<br/>One of the entities has all the components | 0.0008s | **1.0e-06s** |
|
||||
| 1M entities, five components | 0.0010s | **7.0e-07s** |
|
||||
| 1M entities, ten components | 0.0011s | **1.2e-06s** |
|
||||
| 1M entities, ten components<br/>Half of the entities have all the components | 0.0010s | **1.2e-06s** |
|
||||
| 1M entities, ten components<br/>One of the entities has all the components | 0.0008s | **1.2e-06s** |
|
||||
| Sort 150k entities, one component<br/>Arrays are in reverse order | - | **0.0036s** |
|
||||
| Sort 150k entities, enforce permutation<br/>Arrays are in reverse order | - | **0.0005s** |
|
||||
| Sort 150k entities, one component<br/>Arrays are almost sorted, std::sort | - | **0.0035s** |
|
||||
| Sort 150k entities, one component<br/>Arrays are almost sorted, insertion sort | - | **0.0007s** |
|
||||
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 `BUILD_BENCHMARK` option of `CMake` to `ON`, then evaluate yourself whether
|
||||
you're satisfied with the results or not.
|
||||
|
||||
Note: The default version of `EntityX` (`master` branch) wasn't added to the
|
||||
comparison because it's already much slower than its compile-time counterpart.
|
||||
|
||||
Pretty interesting results, aren't them? In fact, these benchmarks are the ones
|
||||
used by `EntityX` to show _how fast it is_. To be honest, they aren't so good
|
||||
and these results shouldn't be taken too seriously (indeed they are completely
|
||||
unrealistic).<br/>
|
||||
The proposed entity-component system is incredibly fast to iterate entities,
|
||||
this is a fact. The compiler can make a lot of optimizations because of how
|
||||
`EnTT` works, even more when components aren't used at all. This is exactly the
|
||||
case for these benchmarks. On the other hand, if we consider real world cases,
|
||||
`EnTT` is somewhere between a bit and much faster than the other solutions
|
||||
around when users also access the components and not just the entities, although
|
||||
it isn't as fast as reported by these benchmarks.<br/>
|
||||
This is why they are completely wrong and cannot be used to evaluate any of the
|
||||
entity-component-system libraries out there.
|
||||
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.
|
||||
|
||||
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.
|
||||
set of features and the general performance, **not** because some single
|
||||
benchmark shows it to be the fastest tool available.
|
||||
|
||||
In the future I'll likely try to get even better performance while still adding
|
||||
new features, mainly for fun.<br/>
|
||||
@@ -239,13 +221,11 @@ supports at least C++17.<br/>
|
||||
The requirements below are mandatory to compile the tests and to extract the
|
||||
documentation:
|
||||
|
||||
* `CMake` version 3.2 or later.
|
||||
* `CMake` version 3.7 or later.
|
||||
* `Doxygen` version 1.8 or later.
|
||||
|
||||
Alternatively, `Bazel` is also supported as a build system (credits to
|
||||
[zaucy](https://github.com/zaucy) who introduced what's required with
|
||||
[this](https://github.com/skypjack/entt/pull/291) pull request and offered to
|
||||
maintain it).<br/>
|
||||
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.
|
||||
|
||||
@@ -290,9 +270,12 @@ The API reference will be created in HTML format within the directory
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
It's also available [online](https://skypjack.github.io/entt/) for the latest
|
||||
version.<br/>
|
||||
Finally, there exists a [wiki](https://github.com/skypjack/entt/wiki) dedicated
|
||||
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
|
||||
@@ -317,16 +300,60 @@ Note that benchmarks are not part of this set.
|
||||
|
||||
`EnTT` is available for some of the most known packaging tools. In particular:
|
||||
|
||||
* [`Conan`](https://bintray.com/skypjack/conan/entt%3Askypjack/_latestVersion),
|
||||
the C/C++ Package Manager for Developers.
|
||||
* [`Conan`](https://github.com/conan-io/conan-center-index), the C/C++ Package
|
||||
Manager for Developers.
|
||||
|
||||
* [`vcpkg`](https://github.com/Microsoft/vcpkg), Microsoft VC++ Packaging
|
||||
Tool.<br/>
|
||||
You can download and install `EnTT` in just a few simple steps:
|
||||
|
||||
```
|
||||
$ git clone https://github.com/Microsoft/vcpkg.git
|
||||
$ cd vcpkg
|
||||
$ ./bootstrap-vcpkg.sh
|
||||
$ ./vcpkg integrate install
|
||||
$ vcpkg install entt
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
* [`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:
|
||||
|
||||
```
|
||||
brew install skypjack/entt/entt
|
||||
```
|
||||
* [`vcpkg`](https://github.com/Microsoft/vcpkg/tree/master/ports/entt),
|
||||
Microsoft VC++ Packaging Tool.
|
||||
|
||||
* [`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:
|
||||
|
||||
```
|
||||
depends: entt ^3.0.0
|
||||
```
|
||||
|
||||
Also check that the configuration refers to a valid repository, so that the
|
||||
package can be found by `build2`:
|
||||
|
||||
* [`cppget.org`](https://cppget.org), the open-source community central
|
||||
repository, accessible as `https://pkg.cppget.org/1/stable`.
|
||||
|
||||
* [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.
|
||||
|
||||
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.
|
||||
|
||||
Consider this list a work in progress and help me to make it longer.
|
||||
|
||||
@@ -375,8 +402,8 @@ know who has participated so far.
|
||||
|
||||
# License
|
||||
|
||||
Code and documentation Copyright (c) 2017-2019 Michele Caini.<br/>
|
||||
Logo Copyright (c) 2018-2019 Richard Caseres.
|
||||
Code and documentation Copyright (c) 2017-2020 Michele Caini.<br/>
|
||||
Logo Copyright (c) 2018-2020 Richard Caseres.
|
||||
|
||||
Code released under
|
||||
[the MIT license](https://github.com/skypjack/entt/blob/master/LICENSE).
|
||||
@@ -390,34 +417,10 @@ Logo released under
|
||||
-->
|
||||
# Support
|
||||
|
||||
## Patreon
|
||||
|
||||
Become a [patron](https://www.patreon.com/bePatron?c=1772573) and get access to
|
||||
extra content, help me spend more time on the projects you love and create new
|
||||
ones for you. Your support will help me to continue the work done so far and
|
||||
make it more professional and feature-rich every day.<br/>
|
||||
It takes very little to
|
||||
[become a patron](https://www.patreon.com/bePatron?c=1772573) and thus help the
|
||||
software you use every day. Don't miss the chance.
|
||||
|
||||
## Donation
|
||||
|
||||
Developing and maintaining `EnTT` takes some time and lots of coffee. I'd like
|
||||
to add more and more functionalities in future and turn it in a full-featured
|
||||
solution.<br/>
|
||||
If you want to support this project, you can offer me an espresso. I'm from
|
||||
Italy, we're used to turning the best coffee ever in code. If you find that
|
||||
it's not enough, feel free to support me the way you prefer.<br/>
|
||||
Take a look at the donation button at the top of the page for more details or
|
||||
just click [here](https://www.paypal.me/skypjack).
|
||||
|
||||
## Hire me
|
||||
|
||||
If you start using `EnTT` and need help, if you want a new feature and want me
|
||||
to give it the highest priority, if you have any other reason to contact me:
|
||||
do not hesitate. I'm available for hiring.<br/>
|
||||
Feel free to take a look at my [profile](https://github.com/skypjack) and
|
||||
contact me by mail.
|
||||
If you want to support this project, you can
|
||||
[offer me](https://github.com/users/skypjack/sponsorship) an espresso.<br/>
|
||||
If you find that it's not enough, feel free to
|
||||
[help me](https://www.paypal.me/skypjack) the way you prefer.
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
51
TODO
51
TODO
@@ -1,32 +1,27 @@
|
||||
* long term feature: templated generic vm
|
||||
* long term feature: shared_ptr less locator
|
||||
* long term feature: shared_ptr less resource cache
|
||||
* long term feature: shared_ptr less locator and resource cache
|
||||
* custom allocators and EnTT allocator-aware in general (long term feature, I don't actually need it at the moment) - see #22
|
||||
* debugging tools (#60): the issue online already contains interesting tips on this, look at it
|
||||
* runner proposal: https://en.wikipedia.org/wiki/Fork%E2%80%93join_model https://slide-rs.github.io/specs/03_dispatcher.html
|
||||
* work stealing job system (see #100)
|
||||
- mt scheduler based on const awareness for types
|
||||
* meta: sort of meta view based on meta stuff to iterate entities, void * and meta info objects
|
||||
* allow for built-in parallel each if possible
|
||||
* work stealing job system (see #100) + mt scheduler based on const awareness for types
|
||||
* meta: sort of meta view based on meta stuff to iterate entities, void * and meta info objects (remove runtime views, welcome reflection)
|
||||
* allow to replace std:: with custom implementations
|
||||
* remove runtime views, welcome reflection and what about snapshot?
|
||||
* types defined at runtime that refer to the same compile-time type (but to different pools) are possible, the library is almost there
|
||||
* add opaque input iterators to views and groups that return tuples <entity, T &...> (proxy), multi-pass guaranteed
|
||||
* add fast lane for raw iterations, extend mt doc to describe allowed add/remove with pre-allocations on fast lanes
|
||||
* registry.each<T...>(first, last) by iterators, entities/components guaranteed
|
||||
* multi component registry::remove and some others?
|
||||
* built-in support for dual (or N-) buffering
|
||||
* allow for custom stomp functions
|
||||
* deprecate/replace snapshot
|
||||
* remove dependency
|
||||
* remove prototype
|
||||
* add examples (and credits) from @alanjfs :)
|
||||
* static reflection, hint: template<> meta_type_t<Type>: meta_descriptor<name, func..., props..., etc...> (see #342)
|
||||
* update documentation for meta, it contains less than half of the actual feature
|
||||
|
||||
TODO
|
||||
* custom (decoupled) pools ==> double buffering, shared components, multi-model
|
||||
* make meta work across boundaries
|
||||
- inline variables are fine here, only the head represents a problem
|
||||
- we should always resolve by looking into the list of types when working across boundaries, no direct resolve
|
||||
* nested groups: AB/ABC/ABCD/... (hints: sort, check functions)
|
||||
* use unordered_map for named pools and context variables:
|
||||
* use direct access (pool-like) also for context variables
|
||||
* improves multi-stomp
|
||||
WIP:
|
||||
* pagination doesn't work nicely across boundaries probably, give it a look. RO operations are fine, adding components maybe not.
|
||||
* make it easier to hook into the type system and describe how to do that to eg auto-generate meta types on first use
|
||||
* add observer functions aside observer class
|
||||
* introduce the component iterators for non-contiguous collections of entities (multi component views, observers, user defined collections)
|
||||
* snapshot: support for range-based archives
|
||||
* update snapshot documentation to describe alternatives
|
||||
* custom pools.
|
||||
* the Perfect Model.
|
||||
* page size 0 -> page less mode
|
||||
* add ::reach and rev iterators to proxy objects for faster and unsafe iterations
|
||||
* add example: 64 bit ids with 32 bits reserved for users' purposes
|
||||
* composable views and "faster views", deprecate non-owning groups
|
||||
* offset instead of pages in the sparse set? top level mask for sparse sets?
|
||||
* add meta dynamic cast (search base for T in parent, we have the meta type already)
|
||||
* make meta base/conv node work with storage/any
|
||||
* deprecate/remove meta_base, meta_conv, ...
|
||||
40
WORKSPACE
40
WORKSPACE
@@ -1,41 +1 @@
|
||||
workspace(name = "com_github_skypjack_entt")
|
||||
|
||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
|
||||
http_archive(
|
||||
name = "com_google_googletest",
|
||||
strip_prefix = "googletest-release-1.8.1",
|
||||
url = "https://github.com/google/googletest/archive/release-1.8.1.tar.gz",
|
||||
sha256 = "9bf1fe5182a604b4135edc1a425ae356c9ad15e9b23f9f12a02e80184c3a249c",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "org_duktape",
|
||||
url = "https://duktape.org/duktape-2.4.0.tar.xz",
|
||||
strip_prefix = "duktape-2.4.0",
|
||||
sha256 = "86a89307d1633b5cedb2c6e56dc86e92679fc34b05be551722d8cc69ab0771fc",
|
||||
build_file_content = """
|
||||
cc_library(
|
||||
name = "duktape",
|
||||
visibility = ["//visibility:public"],
|
||||
strip_include_prefix = "src",
|
||||
hdrs = ["src/duktape.h", "src/duk_config.h"],
|
||||
srcs = ["src/duktape.c", "src/duktape.h", "src/duk_config.h"],
|
||||
)
|
||||
""",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "bazelregistry_cereal",
|
||||
strip_prefix = "cereal-8629f40d932d57c5337d4557327f6f22436211b7",
|
||||
url = "https://github.com/bazelregistry/cereal/archive/8629f40d932d57c5337d4557327f6f22436211b7.zip",
|
||||
sha256 = "c983a7a2e16b153c3de022a0818d2f4836e510a3fc3bea9d3703de79f58a90a6",
|
||||
)
|
||||
|
||||
# This is for bazelregistry_cereal
|
||||
http_archive(
|
||||
name = "bazelregistry_rapidjson",
|
||||
strip_prefix = "rapidjson-6b980984dacf689be8f65be823203b967c69da04",
|
||||
url = "https://github.com/bazelregistry/rapidjson/archive/6b980984dacf689be8f65be823203b967c69da04.zip",
|
||||
sha256 = "82187ba8de53bab3b4fb9e56d0bae81a96fa27a115e5a6ce14c70f0ef9338965",
|
||||
)
|
||||
|
||||
44
appveyor.yml
44
appveyor.yml
@@ -1,44 +0,0 @@
|
||||
# can use variables like {build} and {branch}
|
||||
version: 1.0.{build}
|
||||
|
||||
skip_tags: true
|
||||
|
||||
image:
|
||||
- Visual Studio 2019
|
||||
- Visual Studio 2017
|
||||
|
||||
environment:
|
||||
BUILD_DIR: "%APPVEYOR_BUILD_FOLDER%\\build"
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
|
||||
configuration:
|
||||
- Debug
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
for:
|
||||
-
|
||||
matrix:
|
||||
only:
|
||||
- image: Visual Studio 2019
|
||||
environment:
|
||||
CMAKE_GENERATOR: "Visual Studio 16 2019"
|
||||
-
|
||||
matrix:
|
||||
only:
|
||||
- image: Visual Studio 2017
|
||||
environment:
|
||||
CMAKE_GENERATOR: "Visual Studio 15 2017"
|
||||
|
||||
before_build:
|
||||
- cd %BUILD_DIR%
|
||||
- cmake .. -DBUILD_TESTING=ON -DBUILD_LIB=ON -DCMAKE_CXX_FLAGS=/W1 -G"%CMAKE_GENERATOR%"
|
||||
|
||||
after_build:
|
||||
- ctest --timeout 5 -C Debug -j4
|
||||
|
||||
build:
|
||||
parallel: true
|
||||
project: build/entt.sln
|
||||
verbosity: minimal
|
||||
@@ -1 +0,0 @@
|
||||
exports_files(["copts.bzl"], visibility = ["//:__subpackages__"])
|
||||
@@ -1,10 +0,0 @@
|
||||
_msvc_copts = ["/std:c++17"]
|
||||
_gcc_copts = ["-std=c++17"]
|
||||
|
||||
entt_copts = select({
|
||||
# Windows
|
||||
"@bazel_tools//src/conditions:windows": _msvc_copts,
|
||||
"@bazel_tools//src/conditions:windows_msvc": _msvc_copts,
|
||||
"@bazel_tools//src/conditions:windows_msys": _msvc_copts,
|
||||
"//conditions:default": _gcc_copts,
|
||||
})
|
||||
@@ -1,6 +0,0 @@
|
||||
set(ENTT_VERSION "@PROJECT_VERSION@")
|
||||
set(ENTT_INCLUDE_DIRS "@CMAKE_CURRENT_SOURCE_DIR@/src")
|
||||
|
||||
if(NOT CMAKE_VERSION VERSION_LESS "3.0")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/EnTTTargets.cmake")
|
||||
endif()
|
||||
@@ -1,11 +1,5 @@
|
||||
set(ENTT_VERSION "@PROJECT_VERSION@")
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
set_and_check(ENTT_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
|
||||
|
||||
if(NOT CMAKE_VERSION VERSION_LESS "3.0")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/EnTTTargets.cmake")
|
||||
endif()
|
||||
|
||||
set(EnTT_VERSION "@PROJECT_VERSION@")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/EnTTTargets.cmake")
|
||||
check_required_components("@PROJECT_NAME@")
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
project(cereal-download NONE)
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
include(ExternalProject)
|
||||
|
||||
ExternalProject_Add(
|
||||
cereal
|
||||
GIT_REPOSITORY https://github.com/USCiLab/cereal.git
|
||||
GIT_TAG v1.2.2
|
||||
DOWNLOAD_DIR ${CEREAL_DEPS_DIR}
|
||||
TMP_DIR ${CEREAL_DEPS_DIR}/tmp
|
||||
STAMP_DIR ${CEREAL_DEPS_DIR}/stamp
|
||||
SOURCE_DIR ${CEREAL_DEPS_DIR}/src
|
||||
BINARY_DIR ${CEREAL_DEPS_DIR}/build
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
TEST_COMMAND ""
|
||||
)
|
||||
@@ -1,19 +0,0 @@
|
||||
project(duktape-download NONE)
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
include(ExternalProject)
|
||||
|
||||
ExternalProject_Add(
|
||||
duktape
|
||||
GIT_REPOSITORY https://github.com/svaarala/duktape-releases.git
|
||||
GIT_TAG v2.2.0
|
||||
DOWNLOAD_DIR ${DUKTAPE_DEPS_DIR}
|
||||
TMP_DIR ${DUKTAPE_DEPS_DIR}/tmp
|
||||
STAMP_DIR ${DUKTAPE_DEPS_DIR}/stamp
|
||||
SOURCE_DIR ${DUKTAPE_DEPS_DIR}/src
|
||||
BINARY_DIR ${DUKTAPE_DEPS_DIR}/build
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
TEST_COMMAND ""
|
||||
)
|
||||
@@ -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 ""
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
#ifndef ENTT_CONFIG_VERSION_H
|
||||
#define ENTT_CONFIG_VERSION_H
|
||||
|
||||
|
||||
#define ENTT_VERSION "@PROJECT_VERSION@"
|
||||
#define ENTT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
|
||||
#define ENTT_VERSION_MINOR @PROJECT_VERSION_MINOR@
|
||||
#define ENTT_VERSION_PATCH @PROJECT_VERSION_PATCH@
|
||||
|
||||
|
||||
#endif // ENTT_CONFIG_VERSION_H
|
||||
@@ -4,24 +4,19 @@ 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")
|
||||
username = os.getenv("CONAN_USERNAME")
|
||||
tag_version = os.getenv("CONAN_PACKAGE_VERSION", os.getenv("TRAVIS_TAG"))
|
||||
package_version = tag_version.replace("v", "")
|
||||
package_name_unset = "SET-CONAN_PACKAGE_NAME-OR-CONAN_REFERENCE"
|
||||
package_name = os.getenv("CONAN_PACKAGE_NAME", package_name_unset)
|
||||
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)
|
||||
header_only = os.getenv("CONAN_HEADER_ONLY", False)
|
||||
pure_c = os.getenv("CONAN_PURE_C", False)
|
||||
|
||||
disable_shared = os.getenv("CONAN_DISABLE_SHARED_BUILD", "False")
|
||||
if disable_shared == "True" and package_name == package_name_unset:
|
||||
raise Exception("CONAN_DISABLE_SHARED_BUILD: True is only supported when you define CONAN_PACKAGE_NAME")
|
||||
|
||||
builder = ConanMultiPackager(username=username,
|
||||
reference=reference,
|
||||
@@ -31,10 +26,7 @@ if __name__ == "__main__":
|
||||
stable_branch_pattern=stable_branch_pattern,
|
||||
upload_only_when_stable=upload_only_when_stable,
|
||||
test_folder=test_folder)
|
||||
if header_only == "False":
|
||||
builder.add_common_builds(pure_c=pure_c)
|
||||
else:
|
||||
builder.add()
|
||||
builder.add()
|
||||
|
||||
filtered_builds = []
|
||||
for settings, options, env_vars, build_requires, reference in builder.items:
|
||||
|
||||
@@ -43,8 +43,8 @@ int main() {
|
||||
|
||||
for(auto i = 0; i < 10; ++i) {
|
||||
auto entity = registry.create();
|
||||
registry.assign<position>(entity, i * 1.f, i * 1.f);
|
||||
if(i % 2 == 0) { registry.assign<velocity>(entity, i * .1f, i * .1f); }
|
||||
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);
|
||||
|
||||
2
deps/.gitignore
vendored
2
deps/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -2,6 +2,7 @@
|
||||
# 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})
|
||||
@@ -13,18 +14,9 @@ add_custom_target(
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.cfg
|
||||
WORKING_DIRECTORY ${EnTT_SOURCE_DIR}
|
||||
VERBATIM
|
||||
SOURCES doxy.in
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY ${DOXY_OUTPUT_DIRECTORY}/html
|
||||
DESTINATION share/${PROJECT_NAME}-${PROJECT_VERSION}/
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
docs_aob
|
||||
SOURCES
|
||||
dox/extra.dox
|
||||
md/config.md
|
||||
md/core.md
|
||||
md/entity.md
|
||||
md/faq.md
|
||||
@@ -33,6 +25,13 @@ add_custom_target(
|
||||
md/locator.md
|
||||
md/meta.md
|
||||
md/process.md
|
||||
md/references.md
|
||||
md/resource.md
|
||||
md/signal.md
|
||||
doxy.in
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY ${DOXY_OUTPUT_DIRECTORY}/html
|
||||
DESTINATION share/${PROJECT_NAME}-${PROJECT_VERSION}/
|
||||
)
|
||||
|
||||
228
docs/doxy.in
228
docs/doxy.in
@@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.8.13
|
||||
# Doxyfile 1.8.16
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
@@ -17,11 +17,11 @@
|
||||
# Project related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# This tag specifies the encoding used for all characters in the config file
|
||||
# that follow. The default is UTF-8 which is also the encoding used for all text
|
||||
# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
|
||||
# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
|
||||
# for the list of possible encodings.
|
||||
# This tag specifies the encoding used for all characters in the configuration
|
||||
# file that follow. The default is UTF-8 which is also the encoding used for all
|
||||
# text before the first occurrence of this tag. Doxygen uses libiconv (or the
|
||||
# iconv built into libc) for the transcoding. See
|
||||
# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
|
||||
# The default value is: UTF-8.
|
||||
|
||||
DOXYFILE_ENCODING = UTF-8
|
||||
@@ -93,6 +93,14 @@ ALLOW_UNICODE_NAMES = NO
|
||||
|
||||
OUTPUT_LANGUAGE = English
|
||||
|
||||
# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
|
||||
# documentation generated by doxygen is written. Doxygen will use this
|
||||
# information to generate all generated output in the proper direction.
|
||||
# Possible values are: None, LTR, RTL and Context.
|
||||
# The default value is: None.
|
||||
|
||||
OUTPUT_TEXT_DIRECTION = None
|
||||
|
||||
# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
|
||||
# descriptions after the members that are listed in the file and class
|
||||
# documentation (similar to Javadoc). Set to NO to disable this.
|
||||
@@ -179,6 +187,16 @@ SHORT_NAMES = NO
|
||||
|
||||
JAVADOC_AUTOBRIEF = NO
|
||||
|
||||
# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
|
||||
# such as
|
||||
# /***************
|
||||
# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
|
||||
# Javadoc-style will behave just like regular comments and it will not be
|
||||
# interpreted by doxygen.
|
||||
# The default value is: NO.
|
||||
|
||||
JAVADOC_BANNER = NO
|
||||
|
||||
# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
|
||||
# line (until the first dot) of a Qt-style comment as the brief description. If
|
||||
# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
|
||||
@@ -226,7 +244,12 @@ TAB_SIZE = 4
|
||||
# will allow you to put the command \sideeffect (or @sideeffect) in the
|
||||
# documentation, which will result in a user-defined paragraph with heading
|
||||
# "Side Effects:". You can put \n's in the value part of an alias to insert
|
||||
# newlines.
|
||||
# newlines (in the resulting output). You can put ^^ in the value part of an
|
||||
# alias to insert a newline as if a physical newline was in the original file.
|
||||
# When you need a literal { or } or , in the value part of an alias you have to
|
||||
# escape them by means of a backslash (\), this can lead to conflicts with the
|
||||
# commands \{ and \} for these it is advised to use the version @{ and @} or use
|
||||
# a double escape (\\{ and \\})
|
||||
|
||||
ALIASES =
|
||||
|
||||
@@ -264,17 +287,26 @@ OPTIMIZE_FOR_FORTRAN = NO
|
||||
|
||||
OPTIMIZE_OUTPUT_VHDL = NO
|
||||
|
||||
# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
|
||||
# sources only. Doxygen will then generate output that is more tailored for that
|
||||
# language. For instance, namespaces will be presented as modules, types will be
|
||||
# separated into more groups, etc.
|
||||
# The default value is: NO.
|
||||
|
||||
OPTIMIZE_OUTPUT_SLICE = NO
|
||||
|
||||
# Doxygen selects the parser to use depending on the extension of the files it
|
||||
# parses. With this tag you can assign which parser to use for a given
|
||||
# extension. Doxygen has a built-in mapping, but you can override or extend it
|
||||
# using this tag. The format is ext=language, where ext is a file extension, and
|
||||
# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
|
||||
# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
|
||||
# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
|
||||
# Fortran. In the later case the parser tries to guess whether the code is fixed
|
||||
# or free formatted code, this is the default for Fortran type files), VHDL. For
|
||||
# instance to make doxygen treat .inc files as Fortran files (default is PHP),
|
||||
# and .f files as C (default is Fortran), use: inc=Fortran f=C.
|
||||
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
|
||||
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
|
||||
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
|
||||
# tries to guess whether the code is fixed or free formatted code, this is the
|
||||
# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
|
||||
# .inc files as Fortran files (default is PHP), and .f files as C (default is
|
||||
# Fortran), use: inc=Fortran f=C.
|
||||
#
|
||||
# Note: For files without extension you can use no_extension as a placeholder.
|
||||
#
|
||||
@@ -285,7 +317,7 @@ EXTENSION_MAPPING =
|
||||
|
||||
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
|
||||
# according to the Markdown format, which allows for more readable
|
||||
# documentation. See http://daringfireball.net/projects/markdown/ for details.
|
||||
# documentation. See https://daringfireball.net/projects/markdown/ for details.
|
||||
# The output of markdown processing is further processed by doxygen, so you can
|
||||
# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
|
||||
# case of backward compatibilities issues.
|
||||
@@ -297,10 +329,10 @@ MARKDOWN_SUPPORT = YES
|
||||
# to that level are automatically included in the table of contents, even if
|
||||
# they do not have an id attribute.
|
||||
# Note: This feature currently applies only to Markdown headings.
|
||||
# Minimum value: 0, maximum value: 99, default value: 0.
|
||||
# Minimum value: 0, maximum value: 99, default value: 5.
|
||||
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
|
||||
|
||||
TOC_INCLUDE_HEADINGS = 4
|
||||
TOC_INCLUDE_HEADINGS = 5
|
||||
|
||||
# When enabled doxygen tries to link words that correspond to documented
|
||||
# classes, or namespaces to their corresponding documentation. Such a link can
|
||||
@@ -327,7 +359,7 @@ BUILTIN_STL_SUPPORT = NO
|
||||
CPP_CLI_SUPPORT = NO
|
||||
|
||||
# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
|
||||
# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
|
||||
# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
|
||||
# will parse them like normal C++ but will assume all classes use public instead
|
||||
# of private inheritance when no explicit protection keyword is present.
|
||||
# The default value is: NO.
|
||||
@@ -433,6 +465,12 @@ EXTRACT_ALL = NO
|
||||
|
||||
EXTRACT_PRIVATE = NO
|
||||
|
||||
# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
|
||||
# methods of a class will be included in the documentation.
|
||||
# The default value is: NO.
|
||||
|
||||
EXTRACT_PRIV_VIRTUAL = NO
|
||||
|
||||
# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
|
||||
# scope will be included in the documentation.
|
||||
# The default value is: NO.
|
||||
@@ -511,7 +549,7 @@ INTERNAL_DOCS = NO
|
||||
# names in lower-case letters. If set to YES, upper-case letters are also
|
||||
# allowed. This is useful if you have classes or files whose names only differ
|
||||
# in case and if your file system supports case sensitive file names. Windows
|
||||
# and Mac users are advised to set this option to NO.
|
||||
# (including Cygwin) ands Mac users are advised to set this option to NO.
|
||||
# The default value is: system dependent.
|
||||
|
||||
CASE_SENSE_NAMES = YES
|
||||
@@ -698,7 +736,7 @@ LAYOUT_FILE =
|
||||
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
|
||||
# the reference definitions. This must be a list of .bib files. The .bib
|
||||
# extension is automatically appended if omitted. This requires the bibtex tool
|
||||
# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
|
||||
# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
|
||||
# For LaTeX the style of the bibliography can be controlled using
|
||||
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
|
||||
# search path. See also \cite for info how to create references.
|
||||
@@ -743,7 +781,8 @@ WARN_IF_DOC_ERROR = YES
|
||||
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
|
||||
# are documented, but have no documentation for their parameters or return
|
||||
# value. If set to NO, doxygen will only warn about wrong or incomplete
|
||||
# parameter documentation, but not about the absence of documentation.
|
||||
# parameter documentation, but not about the absence of documentation. If
|
||||
# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_NO_PARAMDOC = YES
|
||||
@@ -781,13 +820,13 @@ WARN_LOGFILE =
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = @DOXY_SOURCE_DIRECTORY@ \
|
||||
@DOXY_DOCS_DIRECTORY@ \
|
||||
@PROJECT_SOURCE_DIR@/README.md
|
||||
@DOXY_DOCS_DIRECTORY@ \
|
||||
@PROJECT_SOURCE_DIR@/README.md
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
|
||||
# documentation (see: http://www.gnu.org/software/libiconv) for the list of
|
||||
# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
|
||||
# possible encodings.
|
||||
# The default value is: UTF-8.
|
||||
|
||||
@@ -805,7 +844,7 @@ INPUT_ENCODING = UTF-8
|
||||
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
|
||||
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
|
||||
# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
|
||||
# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
|
||||
# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
|
||||
FILE_PATTERNS = *.h \
|
||||
*.hpp \
|
||||
@@ -825,7 +864,7 @@ RECURSIVE = YES
|
||||
# Note that relative paths are relative to the directory from which doxygen is
|
||||
# run.
|
||||
|
||||
EXCLUDE =
|
||||
EXCLUDE = @DOXY_DEPS_DIRECTORY@
|
||||
|
||||
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
|
||||
# directories that are symbolic links (a Unix file system feature) are excluded
|
||||
@@ -963,7 +1002,7 @@ INLINE_SOURCES = NO
|
||||
STRIP_CODE_COMMENTS = YES
|
||||
|
||||
# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
|
||||
# function all documented functions referencing it will be listed.
|
||||
# entity all documented functions referencing it will be listed.
|
||||
# The default value is: NO.
|
||||
|
||||
REFERENCED_BY_RELATION = NO
|
||||
@@ -995,12 +1034,12 @@ SOURCE_TOOLTIPS = YES
|
||||
# If the USE_HTAGS tag is set to YES then the references to source code will
|
||||
# point to the HTML generated by the htags(1) tool instead of doxygen built-in
|
||||
# source browser. The htags tool is part of GNU's global source tagging system
|
||||
# (see http://www.gnu.org/software/global/global.html). You will need version
|
||||
# (see https://www.gnu.org/software/global/global.html). You will need version
|
||||
# 4.8.6 or higher.
|
||||
#
|
||||
# To use it do the following:
|
||||
# - Install the latest version of global
|
||||
# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
|
||||
# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
|
||||
# - Make sure the INPUT points to the root of the source tree
|
||||
# - Run doxygen as normal
|
||||
#
|
||||
@@ -1028,7 +1067,7 @@ VERBATIM_HEADERS = YES
|
||||
# rich C++ code for which doxygen's built-in parser lacks the necessary type
|
||||
# information.
|
||||
# Note: The availability of this option depends on whether or not doxygen was
|
||||
# generated with the -Duse-libclang=ON option for CMake.
|
||||
# generated with the -Duse_libclang=ON option for CMake.
|
||||
# The default value is: NO.
|
||||
|
||||
CLANG_ASSISTED_PARSING = NO
|
||||
@@ -1041,6 +1080,16 @@ CLANG_ASSISTED_PARSING = NO
|
||||
|
||||
CLANG_OPTIONS =
|
||||
|
||||
# If clang assisted parsing is enabled you can provide the clang parser with the
|
||||
# path to the compilation database (see:
|
||||
# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files
|
||||
# were built. This is equivalent to specifying the "-p" option to a clang tool,
|
||||
# such as clang-check. These options will then be passed to the parser.
|
||||
# Note: The availability of this option depends on whether or not doxygen was
|
||||
# generated with the -Duse_libclang=ON option for CMake.
|
||||
|
||||
CLANG_DATABASE_PATH =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the alphabetical class index
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -1159,7 +1208,7 @@ HTML_EXTRA_FILES =
|
||||
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
|
||||
# will adjust the colors in the style sheet and background images according to
|
||||
# this color. Hue is specified as an angle on a colorwheel, see
|
||||
# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
|
||||
# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
|
||||
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
|
||||
# purple, and 360 is red again.
|
||||
# Minimum value: 0, maximum value: 359, default value: 220.
|
||||
@@ -1195,6 +1244,17 @@ HTML_COLORSTYLE_GAMMA = 80
|
||||
|
||||
HTML_TIMESTAMP = NO
|
||||
|
||||
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
|
||||
# documentation will contain a main index with vertical navigation menus that
|
||||
# are dynamically created via Javascript. If disabled, the navigation index will
|
||||
# consists of multiple levels of tabs that are statically embedded in every HTML
|
||||
# page. Disable this option to support browsers that do not have Javascript,
|
||||
# like the Qt help browser.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_DYNAMIC_MENUS = YES
|
||||
|
||||
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
|
||||
# documentation will contain sections that can be hidden and shown after the
|
||||
# page has loaded.
|
||||
@@ -1218,13 +1278,13 @@ HTML_INDEX_NUM_ENTRIES = 100
|
||||
|
||||
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
|
||||
# generated that can be used as input for Apple's Xcode 3 integrated development
|
||||
# environment (see: http://developer.apple.com/tools/xcode/), introduced with
|
||||
# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
|
||||
# environment (see: https://developer.apple.com/xcode/), introduced with OSX
|
||||
# 10.5 (Leopard). To create a documentation set, doxygen will generate a
|
||||
# Makefile in the HTML output directory. Running make will produce the docset in
|
||||
# that directory and running make install will install the docset in
|
||||
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
|
||||
# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
|
||||
# for more information.
|
||||
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
|
||||
# genXcode/_index.html for more information.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
@@ -1263,7 +1323,7 @@ DOCSET_PUBLISHER_NAME = Publisher
|
||||
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
|
||||
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
|
||||
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
|
||||
# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
|
||||
# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
|
||||
# Windows.
|
||||
#
|
||||
# The HTML Help Workshop contains a compiler that can convert all HTML output
|
||||
@@ -1339,7 +1399,7 @@ QCH_FILE =
|
||||
|
||||
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
|
||||
# Project output. For more information please see Qt Help Project / Namespace
|
||||
# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
|
||||
# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
|
||||
# The default value is: org.doxygen.Project.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
@@ -1347,7 +1407,7 @@ QHP_NAMESPACE = org.doxygen.Project
|
||||
|
||||
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
|
||||
# Help Project output. For more information please see Qt Help Project / Virtual
|
||||
# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
|
||||
# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
|
||||
# folders).
|
||||
# The default value is: doc.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
@@ -1356,7 +1416,7 @@ QHP_VIRTUAL_FOLDER = doc
|
||||
|
||||
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
|
||||
# filter to add. For more information please see Qt Help Project / Custom
|
||||
# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
|
||||
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
|
||||
# filters).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
@@ -1364,7 +1424,7 @@ QHP_CUST_FILTER_NAME =
|
||||
|
||||
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
|
||||
# custom filter to add. For more information please see Qt Help Project / Custom
|
||||
# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
|
||||
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
|
||||
# filters).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
@@ -1372,7 +1432,7 @@ QHP_CUST_FILTER_ATTRS =
|
||||
|
||||
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
|
||||
# project's filter section matches. Qt Help Project / Filter Attributes (see:
|
||||
# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
|
||||
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_SECT_FILTER_ATTRS =
|
||||
@@ -1465,7 +1525,7 @@ EXT_LINKS_IN_WINDOW = NO
|
||||
|
||||
FORMULA_FONTSIZE = 10
|
||||
|
||||
# Use the FORMULA_TRANPARENT tag to determine whether or not the images
|
||||
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
|
||||
# generated for formulas are transparent PNGs. Transparent PNGs are not
|
||||
# supported properly for IE 6.0, but are supported on all modern browsers.
|
||||
#
|
||||
@@ -1477,7 +1537,7 @@ FORMULA_FONTSIZE = 10
|
||||
FORMULA_TRANSPARENT = YES
|
||||
|
||||
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
|
||||
# http://www.mathjax.org) which uses client side Javascript for the rendering
|
||||
# https://www.mathjax.org) which uses client side Javascript for the rendering
|
||||
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
|
||||
# installed or if you want to formulas look prettier in the HTML output. When
|
||||
# enabled you may also need to install MathJax separately and configure the path
|
||||
@@ -1504,11 +1564,11 @@ MATHJAX_FORMAT = HTML-CSS
|
||||
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
|
||||
# Content Delivery Network so you can quickly see the result without installing
|
||||
# MathJax. However, it is strongly recommended to install a local copy of
|
||||
# MathJax from http://www.mathjax.org before deployment.
|
||||
# The default value is: http://cdn.mathjax.org/mathjax/latest.
|
||||
# MathJax from https://www.mathjax.org before deployment.
|
||||
# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
|
||||
MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/
|
||||
|
||||
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
|
||||
# extension names that should be enabled during MathJax rendering. For example
|
||||
@@ -1566,7 +1626,7 @@ SERVER_BASED_SEARCH = NO
|
||||
#
|
||||
# Doxygen ships with an example indexer (doxyindexer) and search engine
|
||||
# (doxysearch.cgi) which are based on the open source search engine library
|
||||
# Xapian (see: http://xapian.org/).
|
||||
# Xapian (see: https://xapian.org/).
|
||||
#
|
||||
# See the section "External Indexing and Searching" for details.
|
||||
# The default value is: NO.
|
||||
@@ -1579,7 +1639,7 @@ EXTERNAL_SEARCH = NO
|
||||
#
|
||||
# Doxygen ships with an example indexer (doxyindexer) and search engine
|
||||
# (doxysearch.cgi) which are based on the open source search engine library
|
||||
# Xapian (see: http://xapian.org/). See the section "External Indexing and
|
||||
# Xapian (see: https://xapian.org/). See the section "External Indexing and
|
||||
# Searching" for details.
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
@@ -1631,21 +1691,35 @@ LATEX_OUTPUT = latex
|
||||
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
|
||||
# invoked.
|
||||
#
|
||||
# Note that when enabling USE_PDFLATEX this option is only used for generating
|
||||
# bitmaps for formulas in the HTML output, but not in the Makefile that is
|
||||
# written to the output directory.
|
||||
# The default file is: latex.
|
||||
# Note that when not enabling USE_PDFLATEX the default is latex when enabling
|
||||
# USE_PDFLATEX the default is pdflatex and when in the later case latex is
|
||||
# chosen this is overwritten by pdflatex. For specific output languages the
|
||||
# default can have been set differently, this depends on the implementation of
|
||||
# the output language.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_CMD_NAME = latex
|
||||
LATEX_CMD_NAME =
|
||||
|
||||
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
|
||||
# index for LaTeX.
|
||||
# Note: This tag is used in the Makefile / make.bat.
|
||||
# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
|
||||
# (.tex).
|
||||
# The default file is: makeindex.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
MAKEINDEX_CMD_NAME = makeindex
|
||||
|
||||
# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
|
||||
# generate index for LaTeX. In case there is no backslash (\) as first character
|
||||
# it will be automatically added in the LaTeX code.
|
||||
# Note: This tag is used in the generated output file (.tex).
|
||||
# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
|
||||
# The default value is: makeindex.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_MAKEINDEX_CMD = makeindex
|
||||
|
||||
# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
|
||||
# documents. This may be useful for small projects and may help to save some
|
||||
# trees in general.
|
||||
@@ -1766,7 +1840,7 @@ LATEX_SOURCE_CODE = NO
|
||||
|
||||
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
|
||||
# bibliography, e.g. plainnat, or ieeetr. See
|
||||
# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
|
||||
# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
|
||||
# The default value is: plain.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
@@ -1780,6 +1854,14 @@ LATEX_BIB_STYLE = plain
|
||||
|
||||
LATEX_TIMESTAMP = NO
|
||||
|
||||
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
|
||||
# path from which the emoji images will be read. If a relative path is entered,
|
||||
# it will be relative to the LATEX_OUTPUT directory. If left blank the
|
||||
# LATEX_OUTPUT directory will be used.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_EMOJI_DIRECTORY =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the RTF output
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -1819,9 +1901,9 @@ COMPACT_RTF = NO
|
||||
|
||||
RTF_HYPERLINKS = NO
|
||||
|
||||
# Load stylesheet definitions from file. Syntax is similar to doxygen's config
|
||||
# file, i.e. a series of assignments. You only have to provide replacements,
|
||||
# missing definitions are set to their default value.
|
||||
# Load stylesheet definitions from file. Syntax is similar to doxygen's
|
||||
# configuration file, i.e. a series of assignments. You only have to provide
|
||||
# replacements, missing definitions are set to their default value.
|
||||
#
|
||||
# See also section "Doxygen usage" for information on how to generate the
|
||||
# default style sheet that doxygen normally uses.
|
||||
@@ -1830,8 +1912,8 @@ RTF_HYPERLINKS = NO
|
||||
RTF_STYLESHEET_FILE =
|
||||
|
||||
# Set optional variables used in the generation of an RTF document. Syntax is
|
||||
# similar to doxygen's config file. A template extensions file can be generated
|
||||
# using doxygen -e rtf extensionFile.
|
||||
# similar to doxygen's configuration file. A template extensions file can be
|
||||
# generated using doxygen -e rtf extensionFile.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_EXTENSIONS_FILE =
|
||||
@@ -1917,6 +1999,13 @@ XML_OUTPUT = xml
|
||||
|
||||
XML_PROGRAMLISTING = YES
|
||||
|
||||
# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
|
||||
# namespace members in file scope as well, matching the HTML output.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_XML is set to YES.
|
||||
|
||||
XML_NS_MEMB_FILE_SCOPE = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the DOCBOOK output
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -1949,9 +2038,9 @@ DOCBOOK_PROGRAMLISTING = NO
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
|
||||
# AutoGen Definitions (see http://autogen.sf.net) file that captures the
|
||||
# structure of the code including all documentation. Note that this feature is
|
||||
# still experimental and incomplete at the moment.
|
||||
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
|
||||
# the structure of the code including all documentation. Note that this feature
|
||||
# is still experimental and incomplete at the moment.
|
||||
# The default value is: NO.
|
||||
|
||||
GENERATE_AUTOGEN_DEF = NO
|
||||
@@ -2011,7 +2100,7 @@ ENABLE_PREPROCESSING = YES
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
MACRO_EXPANSION = NO
|
||||
MACRO_EXPANSION = YES
|
||||
|
||||
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
|
||||
# the macro expansion is limited to the macros specified with the PREDEFINED and
|
||||
@@ -2118,12 +2207,6 @@ EXTERNAL_GROUPS = YES
|
||||
|
||||
EXTERNAL_PAGES = YES
|
||||
|
||||
# The PERL_PATH should be the absolute path and name of the perl script
|
||||
# interpreter (i.e. the result of 'which perl').
|
||||
# The default file (with absolute path) is: /usr/bin/perl.
|
||||
|
||||
PERL_PATH = /usr/bin/perl
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the dot tool
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -2137,15 +2220,6 @@ PERL_PATH = /usr/bin/perl
|
||||
|
||||
CLASS_DIAGRAMS = YES
|
||||
|
||||
# You can define message sequence charts within doxygen comments using the \msc
|
||||
# command. Doxygen will then run the mscgen tool (see:
|
||||
# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
|
||||
# documentation. The MSCGEN_PATH tag allows you to specify the directory where
|
||||
# the mscgen tool resides. If left empty the tool is assumed to be found in the
|
||||
# default search path.
|
||||
|
||||
MSCGEN_PATH =
|
||||
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
|
||||
109
docs/md/config.md
Normal file
109
docs/md/config.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# Crash Course: configuration
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Definitions](#definitions)
|
||||
* [ENTT_STANDALONE](#entt_standalone)
|
||||
* [ENTT_NOEXCEPT](#entt_noexcept)
|
||||
* [ENTT_HS_SUFFIX and ENTT_HWS_SUFFIX](#entt_hs_suffix_and_entt_hws_suffix)
|
||||
* [ENTT_USE_ATOMIC](#entt_use_atomic)
|
||||
* [ENTT_ID_TYPE](#entt_id_type)
|
||||
* [ENTT_PAGE_SIZE](#entt_page_size)
|
||||
* [ENTT_ASSERT](#entt_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_STANDALONE
|
||||
|
||||
`EnTT` is designed in such a way that it works (almost) everywhere out of the
|
||||
box. However, this is the result of many refinements over time and a compromise
|
||||
regarding some optimizations.<br/>
|
||||
It's worth noting that users can get a small performance boost by passing this
|
||||
definition to the compiler when the library is used in a standalone application.
|
||||
|
||||
## ENTT_NOEXCEPT
|
||||
|
||||
The purpose of this parameter is to suppress the use of `noexcept` by this
|
||||
library.<br/>
|
||||
To do this, simply define the variable without assigning any value to it.
|
||||
|
||||
## ENTT_HS_SUFFIX and ENTT_HWS_SUFFIX
|
||||
|
||||
The `hashed_string` class introduces the `_hs` and `_hws` suffixes to accompany
|
||||
its user defined literals.<br/>
|
||||
In the case of conflicts or even just to change these suffixes, it's possible to
|
||||
do so by associating new ones with these definitions.
|
||||
|
||||
## 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_PAGE_SIZE
|
||||
|
||||
As is known, the ECS module of `EnTT` is based on _sparse sets_. What is less
|
||||
known perhaps is that these are paged to reduce memory consumption in some
|
||||
corner cases.<br/>
|
||||
The default size of a page is 32kB 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 `assert`s for the purpose of
|
||||
detecting errors in debug builds. However, these assertions may in turn affect
|
||||
performance to an extent.<br/>
|
||||
This option is meant to disable all controls.
|
||||
|
||||
## 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
|
||||
|
||||
After many adventures, `EnTT` finally works fine across boundaries.<br/>
|
||||
To do this, the library mixes some non-standard language features with others
|
||||
that are perfectly compliant.<br/>
|
||||
This definition will prevent the library from using non-standard techniques,
|
||||
that is, functionalities that aren't fully compliant with the standard C++.
|
||||
357
docs/md/core.md
357
docs/md/core.md
@@ -6,12 +6,22 @@
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Compile-time identifiers](#compile-time-identifiers)
|
||||
* [Runtime identifiers](#runtime-identifiers)
|
||||
* [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)
|
||||
* [Type support](#type-support)
|
||||
* [Type info](#type-info)
|
||||
* [Almost unique identifiers](#almost-unique-identifiers)
|
||||
* [Type index](#type-index)
|
||||
* [Type traits](#type-traits)
|
||||
* [Member class type](#member-class-type)
|
||||
* [Integral constant](#integral-constant)
|
||||
* [Tag](#tag)
|
||||
* [Utilities](#utilities)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
@@ -23,19 +33,20 @@ 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.
|
||||
|
||||
# Compile-time identifiers
|
||||
# Unique sequential identifiers
|
||||
|
||||
Sometimes it's useful to be able to give unique identifiers to types at
|
||||
compile-time.<br/>
|
||||
There are plenty of different solutions out there and I could have used one of
|
||||
them. However, I decided to spend my time to define a compact and versatile tool
|
||||
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.
|
||||
|
||||
The _result of my efforts_ is the `identifier` class template:
|
||||
## Compile-time generator
|
||||
|
||||
To generate sequential numeric identifiers at compile-time, `EnTT` offers the
|
||||
`identifier` class template:
|
||||
|
||||
```cpp
|
||||
#include <ident.hpp>
|
||||
|
||||
// defines the identifiers for the given types
|
||||
using id = entt::identifier<a_type, another_type>;
|
||||
|
||||
@@ -53,14 +64,14 @@ default:
|
||||
}
|
||||
```
|
||||
|
||||
This is all what the class template has to offer: a `type` inline variable that
|
||||
contains a numerical identifier for the given type. It can be used in any
|
||||
context where constant expressions are required.
|
||||
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 the
|
||||
same for every run. 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:
|
||||
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 {};
|
||||
@@ -72,35 +83,30 @@ using id = entt::identifier<
|
||||
>;
|
||||
```
|
||||
|
||||
A bit ugly to see, but it works at least.
|
||||
Perhaps a bit ugly to see in a codebase but it gets the job done at least.
|
||||
|
||||
# Runtime identifiers
|
||||
## Runtime generator
|
||||
|
||||
Sometimes it's useful to be able to give unique identifiers to types at
|
||||
runtime.<br/>
|
||||
There are plenty of different solutions out there and I could have used one of
|
||||
them. In fact, I adapted the most common one to my requirements and used it
|
||||
extensively within the entire library.
|
||||
|
||||
It's the `family` class. Here is an example of use directly from the
|
||||
entity-component system:
|
||||
To generate sequential numeric identifiers at runtime, `EnTT` offers the
|
||||
`family` class template:
|
||||
|
||||
```cpp
|
||||
using component_family = entt::family<struct internal_registry_component_family>;
|
||||
// defines a custom generator
|
||||
using id = entt::family<struct my_tag>;
|
||||
|
||||
// ...
|
||||
|
||||
template<typename Component>
|
||||
component_type component() const noexcept {
|
||||
return component_family::type<Component>;
|
||||
}
|
||||
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 numerical identifier for the given type.
|
||||
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 the same for every run.
|
||||
Indeed it mostly depends on the flow of execution.
|
||||
Please, note that identifiers aren't guaranteed to be stable across different
|
||||
runs. Indeed it mostly depends on the flow of execution.
|
||||
|
||||
# Hashed strings
|
||||
|
||||
@@ -182,3 +188,284 @@ entt::monostate<"mykey"_hs>{} = 42;
|
||||
const bool b = entt::monostate<"mykey"_hs>{};
|
||||
const int i = entt::monostate<entt::hashed_string{"mykey"}>{};
|
||||
```
|
||||
|
||||
# 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.
|
||||
|
||||
## Type info
|
||||
|
||||
This class template 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.
|
||||
|
||||
Currently, there are a couple of information available:
|
||||
|
||||
* The numeric identifier associated with a given type:
|
||||
|
||||
```cpp
|
||||
auto id = entt::type_info<my_type>::id();
|
||||
```
|
||||
|
||||
In general, the `id` function 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.
|
||||
|
||||
* The _name_ associated with a given type:
|
||||
|
||||
```cpp
|
||||
auto name = entt::type_info<my_type>::name();
|
||||
```
|
||||
|
||||
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. As for the numeric identifier, 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.
|
||||
|
||||
An external type system can also be used if needed. In fact, `type_info` 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_info<Type, std::enable_if_t<has_custom_data_v<Type>>> {
|
||||
static constexpr entt::id_type id() ENTT_NOEXCEPT {
|
||||
return Type::custom_id();
|
||||
}
|
||||
|
||||
static constexpr std::string_view name() ENTT_NOEXCEPT {
|
||||
return Type::custom_name();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Note that this class template and its specializations are widely used within
|
||||
`EnTT`. It also plays a very important role in making `EnTT` work transparently
|
||||
across boundaries in many cases.<br/>
|
||||
Please refer to the dedicated section for more details.
|
||||
|
||||
### Almost unique identifiers
|
||||
|
||||
Since the default non-standard, compile-time implementation makes use of hashed
|
||||
strings, it may happen that two types are assigned the same numeric
|
||||
identifier.<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.<br/>
|
||||
If the types have the same name and belong to the same namespace then their
|
||||
identifiers _could_ be identical (they won't necessarily be the same though).
|
||||
|
||||
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_info` 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 index
|
||||
|
||||
Types in `EnTT` are assigned also unique, sequential _indexes_ generated at
|
||||
runtime:
|
||||
|
||||
```cpp
|
||||
auto index = entt::type_index<my_type>::value();
|
||||
```
|
||||
|
||||
This value may differ from the numeric identifier of a type and 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.
|
||||
|
||||
## 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.
|
||||
|
||||
### 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.
|
||||
|
||||
# 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.
|
||||
|
||||
1177
docs/md/entity.md
1177
docs/md/entity.md
File diff suppressed because it is too large
Load Diff
101
docs/md/faq.md
101
docs/md/faq.md
@@ -12,6 +12,8 @@
|
||||
* [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
|
||||
-->
|
||||
@@ -49,10 +51,15 @@ First of all, there are two things to do in a Windows project:
|
||||
* 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_DISABLE_ASSERT` should be defined to disable internal
|
||||
checks made by `EnTT` in debug. These are asserts introduced to help the users,
|
||||
but require to access to the underlying containers and therefore risk ruining
|
||||
the performance in some cases.
|
||||
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`
|
||||
@@ -94,25 +101,21 @@ performance from this component.
|
||||
|
||||
Custom entity identifiers are definitely a good idea in two cases at least:
|
||||
|
||||
* If `std::uint32_t` isn't large enough as an underlying type.
|
||||
* If `std::uint32_t` is too large or 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.
|
||||
|
||||
These identifiers are nothing more than enum classes with some salt.<br/>
|
||||
To simplify the creation of new identifiers, `EnTT` provides the macro
|
||||
`ENTT_OPAQUE_TYPE` that accepts two arguments:
|
||||
|
||||
* The name you want to give to the new identifier (watch out for namespaces).
|
||||
* The underlying type to use (either `std::uint16_t`, `std::uint32_t`
|
||||
or `std::uint64_t`).
|
||||
|
||||
In fact, this is the definition of `entt::entity`:
|
||||
Identifiers can be defined through enum classes and custom types for which a
|
||||
specialization of `entt_traits` exists. For this purpose, `entt_traits` is also
|
||||
defined as a _sfinae-friendly_ class template.<br/>
|
||||
In fact, this is a definition equivalent to that of `entt::entity`:
|
||||
|
||||
```cpp
|
||||
ENTT_OPAQUE_TYPE(entity, std::uint32_t)
|
||||
enum class entity: std::uint32_t {};
|
||||
```
|
||||
|
||||
The use of this macro is highly recommended, so as not to run into problems if
|
||||
the requirements for the identifiers should change in the future.
|
||||
In theory, integral types can also be used as entity identifiers, even though
|
||||
this may break in future and isn't recommended in general.
|
||||
|
||||
## Warning C4307: integral constant overflow
|
||||
|
||||
@@ -126,13 +129,9 @@ here is a workaround in the form of a macro:
|
||||
|
||||
```cpp
|
||||
#if defined(_MSC_VER)
|
||||
#define HS(str)\
|
||||
__pragma(warning(push))\
|
||||
__pragma(warning(disable:4307))\
|
||||
entt::hashed_string{str}\
|
||||
__pragma(warning(pop))
|
||||
#define HS(str) __pragma(warning(suppress:4307)) entt::hashed_string{str}
|
||||
#else
|
||||
#define HS(str) entt::hashed_string{str}
|
||||
#define HS(str) entt::hashed_string{str}
|
||||
#endif
|
||||
```
|
||||
|
||||
@@ -161,3 +160,59 @@ so as to get rid of the extra definitions:
|
||||
|
||||
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.
|
||||
231
docs/md/lib.md
231
docs/md/lib.md
@@ -5,151 +5,132 @@
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Named types and traits class](#named-types-and-traits-class)
|
||||
* [Do not mix types](#do-not-mix-types)
|
||||
* [Macros, macros everywhere](#macros-macros-everywhere)
|
||||
* [Conflicts](#conflicts)
|
||||
* [Allocations: the dark side of the force](#allocations-the-dark-side-of-the-force)
|
||||
* [Working across boundaries](#working-across-boundaries)
|
||||
* [The EnTT way](#the-entt-way)
|
||||
* [Meta context](#meta-context)
|
||||
* [Memory management](#memory-management)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
# 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 is due mainly to a custom utility used to assign unique, sequential
|
||||
identifiers to different types. Unfortunately, this tool is used by several core
|
||||
classes (the `registry` among the others) that are thus almost unusable across
|
||||
boundaries.<br/>
|
||||
The reasons for that are beyond the purposes of this document. However, the good
|
||||
news is that `EnTT` also offers now a way to overcome this limit and to push
|
||||
things across boundaries without problems when needed.
|
||||
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 straightforward. In
|
||||
fact, everything just works transparently in almost all cases. There are only a
|
||||
few exceptions, easy to deal with anyway.
|
||||
|
||||
# Named types and traits class
|
||||
## The EnTT way
|
||||
|
||||
To allow a type to work properly across boundaries when used by a class that
|
||||
requires to assign unique identifiers to types, users must specialize a class
|
||||
template to literally give a compile-time name to the type itself.<br/>
|
||||
The name of the class template is `name_type_traits` and the specialization must
|
||||
be such that it exposes a static constexpr data member named `value` having type
|
||||
either `ENTT_ID_TYPE` or `entt::hashed_string::hash_type`. Its value is the user
|
||||
defined unique identifier assigned to the specific type.<br/>
|
||||
Identifiers are not to be sequentially generated in this case.
|
||||
Many classes in `EnTT` make extensive use of type erasure for their purposes.
|
||||
This isn't a problem in 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_info` class template is how identifiers are generated and thus made
|
||||
available to the rest of the library. The `type_index` class template makes all
|
||||
types _indexable_ instead, so as to speed up the lookup.
|
||||
|
||||
As an example:
|
||||
In general, these classes don't arouse much interest. The only exceptions are:
|
||||
|
||||
* 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 problem in a concise and elegant way. Please refer to the specific
|
||||
documentation.
|
||||
|
||||
* When working with linked libraries that also export all required symbols.<br/>
|
||||
Compile definitions `ENTT_API_EXPORT` and `ENTT_API_IMPORT` should be passed
|
||||
respectively where there is a need to import or export the symbols defined by
|
||||
`EnTT`, so as to make everything work nicely across boundaries.
|
||||
|
||||
* When working with plugins or shared libraries that don't export any symbol. In
|
||||
this case, `type_index` confuses the other classes by giving potentially wrong
|
||||
information to them.<br/>
|
||||
To avoid problems, it's required to provide a custom generator or to suppress
|
||||
the index generation as a whole:
|
||||
|
||||
```cpp
|
||||
template<typename Type>
|
||||
struct entt::type_index<Type> {};
|
||||
```
|
||||
|
||||
All classes that use `type_index` perform also a check on the possibility of
|
||||
creating indexes for types. If it's not a viable solution, they fallback on
|
||||
the type id provided by `type_info`. The latter makes everything stable across
|
||||
boundaries.<br/>
|
||||
This is why suppressing the generation of the indexes solves the problem. In
|
||||
case it's still necessary to associate sequential indexes with types, users
|
||||
can refer to the `family` class, although knowing that these will not be
|
||||
stable across boundaries.
|
||||
|
||||
For anyone who needs more details, the test suite contains multiple examples
|
||||
covering the most common cases.<br/>
|
||||
It goes without saying that it's impossible to cover all the 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 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
|
||||
struct my_type { /* ... */ };
|
||||
|
||||
template<>
|
||||
struct entt::named_type_traits<my_type> {
|
||||
static constexpr auto value = "my_type"_hs;
|
||||
};
|
||||
entt::meta_ctx ctx{};
|
||||
```
|
||||
|
||||
Because of the rules of the language, the specialization must reside in the
|
||||
global namespace or in the `entt` namespace. There is no way to change this rule
|
||||
unfortunately, because it doesn't depend on the library itself.
|
||||
|
||||
The good aspect of this approach is that it's not intrusive at all. The other
|
||||
way around was in fact forcing users to inherit all their classes from a common
|
||||
base. Something to avoid, at least from my point of view.<br/>
|
||||
However, despite the fact that it's not intrusive, it would be great if it was
|
||||
also easier to use and a bit less error-prone. This is why a bunch of macros
|
||||
exist to ease defining named types.
|
||||
|
||||
## Do not mix types
|
||||
|
||||
Someone might think that this trick is valid only for the types to push across
|
||||
boundaries. This isn't how things work. In fact, the problem is more complex
|
||||
than that.<br/>
|
||||
As a rule of thumb, users should never mix named and non-named types. Whenever
|
||||
a type is given a name, all the types must be given a name. As an example,
|
||||
consider the `registry` class template: in case it is pushed across boundaries,
|
||||
all the types of components should be assigned a name to avoid subtle bugs.
|
||||
|
||||
Indeed, this constraint can be relaxed in many cases. However, it is difficult
|
||||
to define a general rule to follow that is not the most stringent, unless users
|
||||
know exactly what they are doing. Therefore, I won't elaborate on giving further
|
||||
details on the topic.
|
||||
|
||||
# Macros, macros everywhere
|
||||
|
||||
The library comes with a set of predefined macros to use to declare named types
|
||||
or export already existing ones. In particular:
|
||||
|
||||
* `ENTT_NAMED_TYPE` can be used to assign a name to already existing types. This
|
||||
macro must be used in the global namespace even when the types to be named are
|
||||
not.
|
||||
|
||||
```cpp
|
||||
ENTT_NAMED_TYPE(my_type)
|
||||
ENTT_NAMED_TYPE(ns::another_type)
|
||||
```
|
||||
|
||||
* `ENTT_NAMED_STRUCT` can be used to define and export a struct at the same
|
||||
time. It accepts also an optional namespace in which to define the given type.
|
||||
This macro must be used in the global namespace.
|
||||
|
||||
```cpp
|
||||
ENTT_NAMED_STRUCT(my_type, { /* struct definition */})
|
||||
ENTT_NAMED_STRUCT(ns, another_type, { /* struct definition */})
|
||||
```
|
||||
|
||||
* `ENTT_NAMED_CLASS` can be used to define and export a class at the same
|
||||
time. It accepts also an optional namespace in which to define the given type.
|
||||
This macro must be used in the global namespace.
|
||||
|
||||
```cpp
|
||||
ENTT_NAMED_CLASS(my_type, { /* class definition */})
|
||||
ENTT_NAMED_CLASS(ns, another_type, { /* class definition */})
|
||||
```
|
||||
|
||||
Nested namespaces are supported out of the box as well in all cases. As an
|
||||
example:
|
||||
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_NAMED_STRUCT(nested::ns, my_type, { /* struct definition */})
|
||||
entt::meta_ctx::bind(ctx);
|
||||
```
|
||||
|
||||
These macros can be used to avoid specializing the `named_type_traits` class
|
||||
template. In all cases, the name of the class is used also as a seed to generate
|
||||
the compile-time unique identifier.
|
||||
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:
|
||||
|
||||
## Conflicts
|
||||
```cpp
|
||||
entt::meta_ctx::bind{entt::meta_ctx{});
|
||||
```
|
||||
|
||||
When using macros, unique identifiers are 32/64 bit integers generated by
|
||||
hashing strings during compilation. Therefore, conflicts are rare but still
|
||||
possible. In case of conflicts, everything simply will get broken at runtime and
|
||||
the strangest things will probably take place.<br/>
|
||||
Unfortunately, there is no safe way to prevent it. If this happens, it will be
|
||||
enough to give a different value to one of the conflicting types to solve the
|
||||
problem. To do this, users can either assign a different name to the class or
|
||||
directly define a specialization for the `named_type_traits` class template.
|
||||
This is allowed because local and global contexts are separated. Therefore, it's
|
||||
always possible to make the local context the current one again.
|
||||
|
||||
# Allocations: the dark side of the force
|
||||
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.
|
||||
|
||||
As long as `EnTT` won't support custom allocators, another problem with
|
||||
allocations will remain alive instead. This is in fact easily solved, or at
|
||||
least it is if one knows it.
|
||||
## Memory Management
|
||||
|
||||
To allow users to add types dynamically, the library makes extensive use of type
|
||||
erasure techniques and dynamic allocations for pools (whether they are for
|
||||
components, events or anything else). The problem occurs when, for example, a
|
||||
registry is created on one side of a boundary and a pool is dynamically created
|
||||
on the other side. In the best case, everything will crash at the exit, while at
|
||||
worst it will do so at runtime.<br/>
|
||||
To avoid problems, the pools must be generated from the same side of the
|
||||
boundary where the object that owns them is also created. As an example, when
|
||||
the registry is created in the main executable and used across boundaries for a
|
||||
given type of component, the pool for that type must be created before passing
|
||||
around the registry itself. To do this is fortunately quite easy, since it is
|
||||
sufficient to invoke any of the methods that involve the given type (continuing
|
||||
the example with the registry, a call to `reserve` or `size` is more than
|
||||
enough).
|
||||
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.
|
||||
|
||||
Maybe one day some dedicated methods will be added that do nothing but create a
|
||||
pool for a given type. Until now it has been preferred to keep the API cleaner
|
||||
as they are not strictly necessary.
|
||||
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.
|
||||
|
||||
@@ -17,6 +17,10 @@ I hope this list can grow much more in the future:
|
||||
* [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.
|
||||
* [Land of the Rair](https://github.com/LandOfTheRair/core2): the new backend
|
||||
of [a retro-style MUD](https://rair.land/) for the new age.
|
||||
* [Openblack](https://github.com/openblack/openblack): open source
|
||||
reimplementation of the game _Black & White_ (2001).
|
||||
* [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
|
||||
@@ -40,8 +44,19 @@ I hope this list can grow much more in the future:
|
||||
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.
|
||||
|
||||
* Engines and the like:
|
||||
* [Fling Engine](https://github.com/flingengine/FlingEngine): a Vulkan game
|
||||
engine with a focus on data oriented design.
|
||||
* [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
|
||||
@@ -52,14 +67,25 @@ I hope this list can grow much more in the future:
|
||||
[Qub3d](https://qub3d.org/): because blocks should be open source.
|
||||
* [shiva](https://github.com/Milerius/shiva): modern C++ engine with
|
||||
modularity.
|
||||
* [NovusCore](https://github.com/novuscore/NovusCore): A modern take on World
|
||||
* [NovusCore](https://github.com/novuscore/NovusCore): a modern take on World
|
||||
of Warcraft emulation.
|
||||
* [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
|
||||
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.
|
||||
* [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.
|
||||
* [TiltedOnline](https://github.com/tiltedphoques/TiltedOnline) by
|
||||
[Tilted Phoques](https://github.com/tiltedphoques): Skyrim Together, a mod
|
||||
intended to sync the player and the world state accross multiple clients.
|
||||
|
||||
* Articles and blog posts
|
||||
* Articles, videos and blog posts:
|
||||
* [Some posts](https://skypjack.github.io/tags/#entt) on my personal
|
||||
[blog](https://skypjack.github.io/) are about `EnTT`, for those who want to
|
||||
know **more** on this project.
|
||||
@@ -73,23 +99,56 @@ I hope this list can grow much more in the future:
|
||||
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)).
|
||||
* [Game Engine series](https://www.youtube.com/c/TheChernoProject/videos) by
|
||||
[The Cherno](https://github.com/TheCherno):
|
||||
- [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).
|
||||
* [Ability Creator](https://www.erichildebrand.net/blog/ability-creator-project-retrospect):
|
||||
project retrospect by [Eric Hildebrand](https://www.erichildebrand.net/).
|
||||
|
||||
* Any Other Business:
|
||||
* The [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
|
||||
* [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.
|
||||
* [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`).
|
||||
* [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
|
||||
|
||||
629
docs/md/meta.md
629
docs/md/meta.md
@@ -10,6 +10,8 @@
|
||||
* [Reflection in a nutshell](#reflection-in-a-nutshell)
|
||||
* [Any as in any type](#any-as-in-any-type)
|
||||
* [Enjoy the runtime](#enjoy-the-runtime)
|
||||
* [Container support](#container-support)
|
||||
* [Pointer-like types](#pointer-like-types)
|
||||
* [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)
|
||||
@@ -28,11 +30,11 @@ allocations. In one word: unsatisfactory.<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 vice versa.
|
||||
which it belongs and not the opposite.
|
||||
|
||||
# Names and identifiers
|
||||
|
||||
The meta system doesn't force the user to use the tools provided by the library
|
||||
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/>
|
||||
@@ -40,19 +42,19 @@ 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.
|
||||
|
||||
However, the examples in the following sections are all based on the
|
||||
That being said, the examples in the following sections are all based on the
|
||||
`hashed_string` class as provided by this library. Therefore, where an
|
||||
identifier is required, it's likely that a user defined literal is used as
|
||||
follows:
|
||||
|
||||
```cpp
|
||||
auto factory = entt::reflect<my_type>("reflected_type"_hs);
|
||||
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::reflect<my_type>(42);
|
||||
auto factory = entt::meta<my_type>().type(42);
|
||||
```
|
||||
|
||||
Obviously, human-readable identifiers are more convenient to use and highly
|
||||
@@ -63,23 +65,36 @@ recommended.
|
||||
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 _reflect_ a type, the library provides the `reflect` function:
|
||||
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::reflect<my_type>("reflected_type"_hs);
|
||||
auto factory = entt::meta<my_type>();
|
||||
```
|
||||
|
||||
It accepts the type to reflect as a template parameter and an optional
|
||||
identifier as an argument. Identifiers are important because users can retrieve
|
||||
meta types at runtime by searching for them by _name_. However, 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 allow searching the type by _name_.<br/>
|
||||
In both cases, the returned value is a factory object to use to continue
|
||||
building the meta 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:
|
||||
|
||||
A factory is such that all its member functions returns the factory itself.
|
||||
It can be used to extend the reflected type and add the following:
|
||||
```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 returns 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
|
||||
@@ -89,7 +104,7 @@ It can be used to extend the reflected type and add the following:
|
||||
Use the `ctor` member function for this purpose:
|
||||
|
||||
```cpp
|
||||
entt::reflect<my_type>("reflected"_hs).ctor<int, char>().ctor<&factory>();
|
||||
entt::meta<my_type>().ctor<int, char>().ctor<&factory>();
|
||||
```
|
||||
|
||||
* _Destructors_. Free functions can be set as destructors of reflected types.
|
||||
@@ -98,7 +113,7 @@ It can be used to extend the reflected type and add the following:
|
||||
Use the `dtor` member function for this purpose:
|
||||
|
||||
```cpp
|
||||
entt::reflect<my_type>("reflected"_hs).dtor<&destroy>();
|
||||
entt::meta<my_type>().dtor<&destroy>();
|
||||
```
|
||||
|
||||
A function should neither delete nor explicitly invoke the destructor of a
|
||||
@@ -111,7 +126,7 @@ It can be used to extend the reflected type and add the following:
|
||||
Use the `data` member function for this purpose:
|
||||
|
||||
```cpp
|
||||
entt::reflect<my_type>("reflected"_hs)
|
||||
entt::meta<my_type>()
|
||||
.data<&my_type::static_variable>("static"_hs)
|
||||
.data<&my_type::data_member>("member"_hs)
|
||||
.data<&global_variable>("global"_hs);
|
||||
@@ -120,9 +135,15 @@ It can be used to extend the reflected type and add the following:
|
||||
This 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 be set also by means of a couple of functions, namely a
|
||||
setter and a getter. Setters and getters can be either free functions, member
|
||||
functions or mixed ones, as long as they respect the required signatures.<br/>
|
||||
Data members can also be defined by means of a _setter_ and _getter_. 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);
|
||||
```
|
||||
|
||||
Refer to the inline documentation for all the details.
|
||||
|
||||
* _Member functions_. Both real member functions of the underlying type and free
|
||||
@@ -132,7 +153,7 @@ It can be used to extend the reflected type and add the following:
|
||||
Use the `func` member function for this purpose:
|
||||
|
||||
```cpp
|
||||
entt::reflect<my_type>("reflected"_hs)
|
||||
entt::meta<my_type>()
|
||||
.func<&my_type::static_function>("static"_hs)
|
||||
.func<&my_type::member_function>("member"_hs)
|
||||
.func<&free_function>("free"_hs);
|
||||
@@ -148,7 +169,7 @@ It can be used to extend the reflected type and add the following:
|
||||
Use the `base` member function for this purpose:
|
||||
|
||||
```cpp
|
||||
entt::reflect<derived_type>("derived"_hs).base<base_type>();
|
||||
entt::meta<derived_type>().base<base_type>();
|
||||
```
|
||||
|
||||
From now on, wherever a `base_type` is required, an instance of `derived_type`
|
||||
@@ -161,7 +182,7 @@ It can be used to extend the reflected type and add the following:
|
||||
Use the `conv` member function for this purpose:
|
||||
|
||||
```cpp
|
||||
entt::reflect<double>().conv<int>();
|
||||
entt::meta<double>().conv<int>();
|
||||
```
|
||||
|
||||
That's all, everything users need to create meta types and enjoy the reflection
|
||||
@@ -173,7 +194,7 @@ definitely worth the price, at least for me.
|
||||
|
||||
## Any as in any type
|
||||
|
||||
The reflection system comes with its own meta any type. It may seem redundant
|
||||
The reflection system comes with its own `meta_any` type. It may seem redundant
|
||||
since C++17 introduced `std::any`, but it is not.<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
|
||||
@@ -181,39 +202,65 @@ wants to see in a software. Furthermore, the class `std::type_info` suffers from
|
||||
some design flaws and there is even no way to _convert_ an `std::type_info` into
|
||||
a meta type, thus linking the two worlds.
|
||||
|
||||
A meta any object provides an API similar to that of its most famous counterpart
|
||||
and serves the same purpose of being an opaque container for any type of
|
||||
value.<br/>
|
||||
The class `meta_any` offers an API similar to that of its most famous
|
||||
counterpart and serves the same purpose of being an opaque container for any
|
||||
type of value.<br/>
|
||||
It minimizes the allocations required, which are almost absent thanks to _SBO_
|
||||
techniques. In fact, unless users deal with _fat types_ and create instances of
|
||||
them though the reflection system, allocations are at zero.
|
||||
them through the reflection system, allocations are at zero.
|
||||
|
||||
A meta any object can be created by any other object or as an empty container
|
||||
to initialize later:
|
||||
Creating instances of `meta_any`, whether empty or from existing objects, is
|
||||
trivial:
|
||||
|
||||
```cpp
|
||||
// a meta any object that contains an int
|
||||
// a container for an int
|
||||
entt::meta_any any{0};
|
||||
|
||||
// an empty meta any object
|
||||
// an empty container
|
||||
entt::meta_any empty{};
|
||||
```
|
||||
|
||||
It takes the burden of destroying the contained instance when required.<br/>
|
||||
Moreover, it can be used as an opaque container for unmanaged objects if needed:
|
||||
The `meta_any` class takes also the burden of destroying the contained object
|
||||
when required.<br/>
|
||||
Furthermore, an instance of `meta_any` is not tied to a specific 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.
|
||||
|
||||
A particularly interesting feature of this class is that it can also be used as
|
||||
an opaque container for non-const unmanaged objects:
|
||||
|
||||
```cpp
|
||||
int value;
|
||||
entt::meta_any any{entt::as_alias, value};
|
||||
entt::meta_any any{std::ref(value)};
|
||||
```
|
||||
|
||||
In this case, the contained instance is never destroyed and users must ensure
|
||||
that the lifetime of the object exceeds that of the container.
|
||||
In other words, whenever `meta_any` intercepts a `reference_wrapper`, it acts as
|
||||
a reference to the original instance rather than making a copy of it. 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 `meta_any` from existing
|
||||
ones:
|
||||
|
||||
A meta any object has a `type` member function that returns the meta type of the
|
||||
contained value, if any. The member functions `try_cast`, `cast` and `convert`
|
||||
are used to know if the underlying object has a given type as a base or if it
|
||||
can be converted implicitly to it.
|
||||
```cpp
|
||||
// aliasing constructor
|
||||
entt::meta_any ref = any.ref();
|
||||
```
|
||||
|
||||
In this case, it doesn't matter if the starting 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/>
|
||||
It means that, starting from the example above, both `ref` and` any` will point
|
||||
to the same object, whether it's initially contained in `any` or already an
|
||||
unmanaged one. This is particularly useful for passing instances of `meta_any`
|
||||
belonging to the external context by reference to a function or a constructor
|
||||
rather than making copies of them.
|
||||
|
||||
The `meta_any` class also has a `type` member function that returns the meta
|
||||
type of the contained value, if any. The member functions `try_cast`, `cast` and
|
||||
`convert` are then used to know if the underlying object has a given type as a
|
||||
base or if it can be converted implicitly to it.
|
||||
|
||||
## Enjoy the runtime
|
||||
|
||||
@@ -223,30 +270,35 @@ All this has the great merit that, unlike the vast majority of the things
|
||||
present in this library and closely linked to the compile-time, the reflection
|
||||
system stands in fact as a non-intrusive tool for the runtime.
|
||||
|
||||
To search for a reflected type there are two options: by type or by _name_. In
|
||||
both cases, the search can be done by means of the `resolve` function:
|
||||
To search for a reflected type there are a few options:
|
||||
|
||||
```cpp
|
||||
// search for a reflected type by type
|
||||
// direct access to a reflected type
|
||||
auto by_type = entt::resolve<my_type>();
|
||||
|
||||
// search for a reflected type by name
|
||||
auto by_name = entt::resolve("reflected_type"_hs);
|
||||
// lookup of a reflected type by identifier
|
||||
auto by_id = entt::resolve_id("reflected_type"_hs);
|
||||
|
||||
// lookup of a reflected type by type id
|
||||
auto by_type_id = entt::resolve_type(entt::type_info<my_type>::id());
|
||||
```
|
||||
|
||||
There exits also a third overload of the `resolve` function to use to iterate
|
||||
all the reflected types at once:
|
||||
There exits also an overload of the `resolve` function to use to iterate all the
|
||||
reflected types at once as well as a `resolve_if` function to use to perform
|
||||
more refined searches when needed:
|
||||
|
||||
```cpp
|
||||
resolve([](auto type) {
|
||||
// ...
|
||||
});
|
||||
|
||||
auto by_lookup = resolve_if([](auto type) { return type.is_floating_point(); });
|
||||
```
|
||||
|
||||
In all cases, the returned value is an instance of `meta_type`. This type of
|
||||
objects offer an API to know the _runtime identifier_ of the type, to iterate
|
||||
all the meta objects associated with them and even to build or destroy instances
|
||||
of the underlying type.<br/>
|
||||
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:
|
||||
@@ -260,20 +312,9 @@ The meta objects that compose a meta type are accessed in the following ways:
|
||||
The returned type is `meta_ctor` and may be invalid if there is no constructor
|
||||
that accepts the supplied arguments or at least some types from which they are
|
||||
derived or to which they can be converted.<br/>
|
||||
A meta constructor offers an API to know the number of arguments, the expected
|
||||
meta types and to invoke it, therefore to construct a new instance of the
|
||||
underlying type.
|
||||
|
||||
* _Meta destructor_. It's returned by a dedicated function:
|
||||
|
||||
```cpp
|
||||
auto dtor = entt::resolve<my_type>().dtor();
|
||||
```
|
||||
|
||||
The returned type is `meta_dtor` and may be invalid if there is no custom
|
||||
destructor set for the given meta type.<br/>
|
||||
All what a meta destructor has to offer is a way to invoke it on a given
|
||||
instance. Be aware that the result may not be what is expected.
|
||||
A meta constructor offers an API to know the number of its arguments and their
|
||||
expected meta types. Furthermor, it's possible to invoke it and therefore to
|
||||
construct new instances of the underlying type.
|
||||
|
||||
* _Meta data_. They are accessed by _name_:
|
||||
|
||||
@@ -283,9 +324,9 @@ The meta objects that compose a meta type are accessed in the following ways:
|
||||
|
||||
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 (ie 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.
|
||||
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_:
|
||||
|
||||
@@ -295,11 +336,11 @@ The meta objects that compose a meta type are accessed in the following ways:
|
||||
|
||||
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 (ie 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 meta any object.
|
||||
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:
|
||||
|
||||
@@ -323,23 +364,20 @@ The meta objects that compose a meta type are accessed in the following ways:
|
||||
conversion function associated with the given type.<br/>
|
||||
The meta conversion functions are as thin as the meta bases and with a very
|
||||
similar interface. The sole difference is that they return a newly created
|
||||
instance wrapped in a meta any object when they convert between different
|
||||
instance wrapped in a `meta_any` object when they convert between different
|
||||
types.
|
||||
|
||||
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
|
||||
auto func = entt::resolve<my_type>().func("member"_hs);
|
||||
|
||||
if(func) {
|
||||
if(auto func = entt::resolve<my_type>().func("member"_hs); func) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Furthermore, all meta objects with the exception of meta destructors can be
|
||||
iterated through an overload that accepts a callback through which to return
|
||||
them. As an example:
|
||||
Furthermore, all meta objects can be iterated through an overload that accepts a
|
||||
callback through which to return them. As an example:
|
||||
|
||||
```cpp
|
||||
entt::resolve<my_type>().data([](auto data) {
|
||||
@@ -347,23 +385,302 @@ entt::resolve<my_type>().data([](auto data) {
|
||||
});
|
||||
```
|
||||
|
||||
A meta type can also be used to `construct` or `destroy` actual instances of the
|
||||
underlying type.<br/>
|
||||
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 returns a `meta_any` object that may or
|
||||
may not be initialized, depending on whether a suitable constructor has been
|
||||
found or not. On the other side, the `destroy` member function accepts instances
|
||||
of `meta_any` as well as actual objects by reference and invokes the registered
|
||||
destructor if any.<br/>
|
||||
Be aware that the result of a call to `destroy` may not be what is expected. The
|
||||
purpose is to give users the ability to free up resources that require special
|
||||
treatment and **not** to actually destroy instances.
|
||||
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. The reason is quickly explained: 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/>
|
||||
Therefore, exposing destructors would be pointless and would add nothing to the
|
||||
library itself.
|
||||
|
||||
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 official documentation to get the best out of this powerful tool.
|
||||
read the inline documentation to get the best out of this powerful tool.
|
||||
|
||||
## Container support
|
||||
|
||||
The meta module supports containers of all types out of the box.<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 by the meta module, 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{std::ref(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.handle(), 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 deliberately minimal but theoretically 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`, 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::uniqe_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 containers 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_meta_pointer_like()) {
|
||||
// meta type equivalent to that of int
|
||||
if(entt::meta_any ref = *any; ref) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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 returned
|
||||
object is valid. For example, invalid instances are returned when the wrapped
|
||||
object hasn'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.
|
||||
|
||||
## Policies: the more, the less
|
||||
|
||||
@@ -391,21 +708,19 @@ There are a few alternatives available at the moment:
|
||||
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.
|
||||
In the second case, instead, the property will not be accessible for reading.
|
||||
|
||||
As an example of use:
|
||||
|
||||
```cpp
|
||||
entt::reflect<my_type>("reflected"_hs)
|
||||
.func<&my_type::member_function, entt::as_void_t>("member"_hs);
|
||||
entt::meta<my_type>().func<&my_type::member_function, entt::as_void_t>("member"_hs);
|
||||
```
|
||||
|
||||
* The _as-alias_ policy, associated with the type `entt::as_alias_t`.<br/>
|
||||
It allows to build wrappers that act as aliases for the objects used to
|
||||
initialize them. Modifying the object contained in the wrapper for which the
|
||||
_aliasing_ was requested will make it possible to directly modify the instance
|
||||
used to initialize the wrapper itself.<br/>
|
||||
* The _as-ref_ policy, associated with the type `entt::as_ref_t`.<br/>
|
||||
It allows to build wrappers that act as references to unmanaged objects.
|
||||
Modifying the object contained in the wrapper for which the _reference_ was
|
||||
requested will make it possible to directly modify the instance used to
|
||||
initialize the wrapper itself.<br/>
|
||||
This policy works with constructors (for example, when objects are taken from
|
||||
an external container rather than created on demand), data members and
|
||||
functions in general (as long as their return types are lvalue references).
|
||||
@@ -413,8 +728,7 @@ There are a few alternatives available at the moment:
|
||||
As an example of use:
|
||||
|
||||
```cpp
|
||||
entt::reflect<my_type>("reflected"_hs)
|
||||
.data<&my_type::data_member, entt::as_alias_t>("member"_hs);
|
||||
entt::meta<my_type>().data<&my_type::data_member, entt::as_ref_t>("member"_hs);
|
||||
```
|
||||
|
||||
Some uses are rather trivial, but it's useful to note that there are some less
|
||||
@@ -439,11 +753,11 @@ members of the reflected types.
|
||||
Exporting constant values or elements from an enum is as simple as ever:
|
||||
|
||||
```cpp
|
||||
entt::reflect<my_enum>()
|
||||
entt::meta<my_enum>()
|
||||
.data<my_enum::a_value>("a_value"_hs)
|
||||
.data<my_enum::another_value>("another_value"_hs);
|
||||
|
||||
entt::reflect<int>().data<2048>("max_int"_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
|
||||
@@ -455,29 +769,90 @@ 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
|
||||
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 be able to attach properties to the meta objects created. Fortunately, this
|
||||
is possible for most of them.<br/>
|
||||
To attach a property to a meta object, no matter what as long as it supports
|
||||
properties, it is sufficient to provide an object at the time of construction
|
||||
such that `std::get<0>` and `std::get<1>` are valid for it. In other terms, the
|
||||
properties are nothing more than key/value pairs users can put in an
|
||||
`std::pair`. As an example:
|
||||
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::reflect<my_type>("reflected"_hs, std::make_pair("tooltip"_hs, "message"));
|
||||
entt::meta<my_type>().type("reflected_type"_hs).prop("tooltip"_hs, "message");
|
||||
```
|
||||
|
||||
The meta objects that support properties offer then a couple of member functions
|
||||
named `prop` to iterate them at once and to search a specific property by key:
|
||||
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.
|
||||
|
||||
* Annotations:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().type("reflected_type"_hs).prop(&property_generator);
|
||||
```
|
||||
|
||||
An annotation is an invocable object that returns one or more properties. All
|
||||
of them are treated individually.
|
||||
|
||||
It's possible to invoke the `prop` function several times if needed, one for
|
||||
each property to associate with the last meta object created:
|
||||
|
||||
```cpp
|
||||
// iterate all the properties of a meta type
|
||||
entt::meta<my_type>()
|
||||
.type("reflected_type"_hs)
|
||||
.prop(entt::hashed_string{"Name"}, "Reflected Type")
|
||||
.data<&my_type::data_member>("member"_hs)
|
||||
.prop(std::make_pair("tooltip"_hs, "Member"))
|
||||
.prop(my_enum::a_value, 42);
|
||||
```
|
||||
|
||||
Alternatively, the `props` function is available to associate several properties
|
||||
at a time. However, 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 the meta
|
||||
types, meta constructors, meta data and meta functions. It's not possible to
|
||||
attach properties to other types of meta objects and the factory returned as a
|
||||
result of their construction won't allow such an operation.
|
||||
|
||||
These types 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
|
||||
entt::resolve<my_type>().prop([](auto prop) {
|
||||
// ...
|
||||
});
|
||||
@@ -488,7 +863,7 @@ 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.
|
||||
the key and the value contained in the form of `meta_any` objects, respectively.
|
||||
|
||||
## Unregister types
|
||||
|
||||
@@ -497,15 +872,13 @@ means unregistering all its data members, member functions, conversion functions
|
||||
and so on. However, the base classes won't be unregistered, 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) won't
|
||||
be unregistered.
|
||||
|
||||
To unregister a type, users can use the `unregister` function from the global
|
||||
namespace:
|
||||
be 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::unregister<my_type>();
|
||||
entt::resolve<my_type>().reset();
|
||||
```
|
||||
|
||||
This function returns a boolean value that is true if the type is actually
|
||||
registered with the reflection system, false otherwise.<br/>
|
||||
The type can be re-registered later with a completely different name and form.
|
||||
|
||||
@@ -74,6 +74,10 @@ Here is a minimal example for the sake of curiosity:
|
||||
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);
|
||||
|
||||
@@ -85,7 +89,7 @@ struct my_process: entt::process<my_process, std::uint32_t> {
|
||||
}
|
||||
|
||||
private:
|
||||
delta_type remaining{1000u};
|
||||
delta_type remaining;
|
||||
};
|
||||
```
|
||||
|
||||
@@ -158,7 +162,7 @@ To attach a process to a scheduler there are mainly two ways:
|
||||
the `attach` member function:
|
||||
|
||||
```cpp
|
||||
scheduler.attach<my_process>("foobar");
|
||||
scheduler.attach<my_process>(1000u);
|
||||
```
|
||||
|
||||
* Otherwise, in case of a lambda or a functor, it's enough to provide an
|
||||
@@ -182,7 +186,7 @@ scheduler.attach([](auto delta, void *, auto succeed, auto fail) {
|
||||
// ...
|
||||
})
|
||||
// appends a child in the form of a process class
|
||||
.then<my_process>();
|
||||
.then<my_process>(1000u);
|
||||
```
|
||||
|
||||
To update a scheduler and therefore all its processes, the `update` member
|
||||
|
||||
46
docs/md/references.md
Normal file
46
docs/md/references.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# 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:
|
||||
* [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.
|
||||
|
||||
* Go:
|
||||
* [gecs](https://github.com/tutumagi/gecs): a sparse sets based ECS inspired
|
||||
by `EnTT`.
|
||||
|
||||
* Javascript:
|
||||
* [ecsy](https://github.com/MozillaReality/ecsy): I haven't had the time to
|
||||
investigate the underlying design of `ecsy` but it looks cool anyway.
|
||||
|
||||
* 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.
|
||||
|
||||
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.
|
||||
@@ -42,7 +42,7 @@ 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 the dedicated base class as in the following example:
|
||||
inherit directly from a dedicated base class as in the following example:
|
||||
|
||||
```cpp
|
||||
struct my_loader final: entt::resource_loader<my_loader, my_resource> {
|
||||
@@ -76,11 +76,11 @@ Finally, a cache is a specialization of a class template tailored to a specific
|
||||
resource:
|
||||
|
||||
```cpp
|
||||
using my_resource_cache = entt::resource_cache<my_resource>;
|
||||
using my_cache = entt::resource_cache<my_resource>;
|
||||
|
||||
// ...
|
||||
|
||||
my_resource_cache cache{};
|
||||
my_cache cache{};
|
||||
```
|
||||
|
||||
The idea is to create different caches for different types of resources and to
|
||||
@@ -105,21 +105,14 @@ cache.clear();
|
||||
|
||||
Besides these member functions, a cache contains what is needed to load, use and
|
||||
discard resources of the given type.<br/>
|
||||
Before to explore this part of the interface, it makes sense to mention how
|
||||
resources are identified. The type of the identifiers to use is defined as:
|
||||
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
|
||||
entt::resource_cache<resource>::resource_type
|
||||
```
|
||||
|
||||
Where `resource_type` is an alias for `entt::hashed_string::hash_type`.
|
||||
Therefore, resource identifiers are created explicitly as in the following
|
||||
example:
|
||||
|
||||
```cpp
|
||||
constexpr auto identifier = entt::resource_cache<resource>::resource_type{"my/resource/identifier"_hs};
|
||||
constexpr auto identifier = "my/resource/identifier"_hs;
|
||||
// this is equivalent to the following
|
||||
constexpr auto hs = entt::hashed_string{"my/resource/identifier"};
|
||||
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
|
||||
@@ -142,7 +135,7 @@ 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(auto handle = cache.load<my_loader>("another/identifier"_hs, 42); handle) {
|
||||
if(entt::resource_handle handle = cache.load<my_loader>("another/identifier"_hs, 42); handle) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
@@ -187,24 +180,24 @@ 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 const reference on request:
|
||||
resource as a (possibly const) reference on request:
|
||||
|
||||
* By means of the `get` member function:
|
||||
|
||||
```cpp
|
||||
const auto &resource = handle.get();
|
||||
auto &resource = handle.get();
|
||||
```
|
||||
|
||||
* Using the proper cast operator:
|
||||
|
||||
```cpp
|
||||
const auto &resource = handle;
|
||||
auto &resource = handle;
|
||||
```
|
||||
|
||||
* Through the dereference operator:
|
||||
|
||||
```cpp
|
||||
const auto &resource = *handle;
|
||||
auto &resource = *handle;
|
||||
```
|
||||
|
||||
The resource can also be accessed directly using the arrow operator if required:
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Delegate](#delegate)
|
||||
* [Runtime arguments](#runtime-arguments)
|
||||
* [Lambda support](#lambda-support)
|
||||
* [Signals](#signals)
|
||||
* [Event dispatcher](#event-dispatcher)
|
||||
* [Event emitter](#event-emitter)
|
||||
@@ -37,7 +39,7 @@ lightweight classes to solve the same and many other problems.
|
||||
A delegate can be used as a general purpose invoker with no memory overhead for
|
||||
free functions and members provided along with an instance on which to invoke
|
||||
them.<br/>
|
||||
It does not claim to be a drop-in replacement for an `std::function`, so do not
|
||||
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.
|
||||
@@ -63,7 +65,7 @@ As an example of use:
|
||||
int f(int i) { return i; }
|
||||
|
||||
struct my_struct {
|
||||
int f(const int &i) { return i }
|
||||
int f(const int &i) const { return i; }
|
||||
};
|
||||
|
||||
// bind a free function to the delegate
|
||||
@@ -79,9 +81,9 @@ 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. In this case, `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:
|
||||
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) { /* ... */ }
|
||||
@@ -107,7 +109,7 @@ 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/>v
|
||||
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.
|
||||
|
||||
@@ -142,6 +144,97 @@ 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:
|
||||
|
||||
```
|
||||
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
|
||||
@@ -158,11 +251,10 @@ _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. The most important thing is that
|
||||
it comes in two forms: with and without a collector. In case a signal is
|
||||
provided with a collector, all the values returned by the listeners can be
|
||||
literally _collected_ and used later by the caller. Otherwise it works just like
|
||||
a plain signal that emits events from time to time.<br/>
|
||||
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:
|
||||
|
||||
@@ -200,10 +292,10 @@ sink.disconnect<&foo>();
|
||||
// disconnect a member function of an instance
|
||||
sink.disconnect<&listener::bar>(instance);
|
||||
|
||||
// disconnect all the member functions of an instance, if any
|
||||
// disconnect all member functions of an instance, if any
|
||||
sink.disconnect(instance);
|
||||
|
||||
// discards all the listeners at once
|
||||
// discards all listeners at once
|
||||
sink.disconnect();
|
||||
```
|
||||
|
||||
@@ -211,10 +303,20 @@ 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/>
|
||||
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.
|
||||
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`
|
||||
@@ -285,9 +387,9 @@ 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
|
||||
`const E &` can be converted to them for each type of event `E`, no matter what
|
||||
the return value is.<br/>
|
||||
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:
|
||||
|
||||
@@ -345,7 +447,7 @@ 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<my_event>();
|
||||
dispatcher.update<an_event>();
|
||||
|
||||
// emits all the events queued so far at once
|
||||
dispatcher.update();
|
||||
@@ -375,9 +477,9 @@ 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 const 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/>
|
||||
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/>
|
||||
@@ -391,10 +493,10 @@ my_emitter emitter{};
|
||||
```
|
||||
|
||||
Listeners must be movable and callable objects (free functions, lambdas,
|
||||
functors, `std::function`s, whatever) whose function type is:
|
||||
functors, `std::function`s, whatever) whose function type is compatible with:
|
||||
|
||||
```cpp
|
||||
void(const Event &, my_emitter &)
|
||||
void(Event &, my_emitter &)
|
||||
```
|
||||
|
||||
Where `Event` is the type of event they want to listen.<br/>
|
||||
|
||||
@@ -41,14 +41,14 @@ echo "Sedding..."
|
||||
|
||||
# change the url in the formula file
|
||||
# the slashes in the URL must be escaped
|
||||
ESCAPED_URL="$(sed -e 's/[\/&]/\\&/g' <<< "$URL")"
|
||||
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 "$FORMULA-e"
|
||||
rm -rf "$FORMULA-e"
|
||||
|
||||
# update remote repo
|
||||
echo "Gitting..."
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
scripts/update_homebrew.sh $1
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,45 +3,78 @@
|
||||
|
||||
|
||||
#ifndef ENTT_NOEXCEPT
|
||||
#define ENTT_NOEXCEPT noexcept
|
||||
#endif // ENTT_NOEXCEPT
|
||||
# define ENTT_NOEXCEPT noexcept
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef ENTT_HS_SUFFIX
|
||||
#define ENTT_HS_SUFFIX _hs
|
||||
#endif // ENTT_HS_SUFFIX
|
||||
# define ENTT_HS_SUFFIX _hs
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef ENTT_HWS_SUFFIX
|
||||
#define ENTT_HWS_SUFFIX _hws
|
||||
#endif // ENTT_HWS_SUFFIX
|
||||
# define ENTT_HWS_SUFFIX _hws
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef ENTT_NO_ATOMIC
|
||||
#include <atomic>
|
||||
#define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
|
||||
#else // ENTT_NO_ATOMIC
|
||||
#define ENTT_MAYBE_ATOMIC(Type) Type
|
||||
#endif // ENTT_NO_ATOMIC
|
||||
#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 // ENTT_ID_TYPE
|
||||
# include <cstdint>
|
||||
# define ENTT_ID_TYPE std::uint32_t
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef ENTT_PAGE_SIZE
|
||||
#define ENTT_PAGE_SIZE 32768
|
||||
#endif // ENTT_PAGE_SIZE
|
||||
# define ENTT_PAGE_SIZE 32768
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef ENTT_DISABLE_ASSERT
|
||||
#include <cassert>
|
||||
#define ENTT_ASSERT(condition) assert(condition)
|
||||
#else // ENTT_DISABLE_ASSERT
|
||||
#define ENTT_ASSERT(...) ((void)0)
|
||||
#endif // ENTT_DISABLE_ASSERT
|
||||
#ifndef ENTT_ASSERT
|
||||
# include <cassert>
|
||||
# define ENTT_ASSERT(condition) assert(condition)
|
||||
#endif
|
||||
|
||||
|
||||
#endif // ENTT_CONFIG_CONFIG_H
|
||||
#ifndef ENTT_NO_ETO
|
||||
# include <type_traits>
|
||||
# define ENTT_IS_EMPTY(Type) std::is_empty<Type>
|
||||
#else
|
||||
# include <type_traits>
|
||||
# define ENTT_IS_EMPTY(Type) std::false_type
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef ENTT_STANDARD_CPP
|
||||
# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8)
|
||||
# define ENTT_PRETTY_FUNCTION_CONSTEXPR
|
||||
# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
|
||||
# define ENTT_PRETTY_FUNCTION_PREFIX '='
|
||||
# define ENTT_PRETTY_FUNCTION_SUFFIX ']'
|
||||
# elif 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_CONSTEXPR
|
||||
# define ENTT_PRETTY_FUNCTION __FUNCSIG__
|
||||
# define ENTT_PRETTY_FUNCTION_PREFIX '<'
|
||||
# define ENTT_PRETTY_FUNCTION_SUFFIX '>'
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef ENTT_STANDALONE
|
||||
# define ENTT_FAST_PATH(...) false
|
||||
#else
|
||||
# define ENTT_FAST_PATH(Cond) Cond
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
#define ENTT_CONFIG_VERSION_H
|
||||
|
||||
|
||||
#define ENTT_VERSION "3.1.0"
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 1
|
||||
#define ENTT_VERSION_MINOR 5
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
|
||||
|
||||
#endif // ENTT_CONFIG_VERSION_H
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define ENTT_CORE_ALGORITHM_HPP
|
||||
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
@@ -79,7 +80,7 @@ struct insertion_sort {
|
||||
*/
|
||||
template<std::size_t Bit, std::size_t N>
|
||||
struct radix_sort {
|
||||
static_assert((N % Bit) == 0);
|
||||
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.
|
||||
@@ -140,4 +141,4 @@ struct radix_sort {
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_CORE_ALGORITHM_HPP
|
||||
#endif
|
||||
|
||||
33
src/entt/core/attribute.h
Normal file
33
src/entt/core/attribute.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#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
|
||||
@@ -2,8 +2,8 @@
|
||||
#define ENTT_CORE_FAMILY_HPP
|
||||
|
||||
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
@@ -18,24 +18,20 @@ namespace entt {
|
||||
*/
|
||||
template<typename...>
|
||||
class family {
|
||||
inline static ENTT_MAYBE_ATOMIC(ENTT_ID_TYPE) identifier;
|
||||
|
||||
template<typename...>
|
||||
// clang (since version 9) started to complain if auto is used instead of ENTT_ID_TYPE
|
||||
inline static const ENTT_ID_TYPE inner = identifier++;
|
||||
inline static ENTT_MAYBE_ATOMIC(id_type) identifier{};
|
||||
|
||||
public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using family_type = ENTT_ID_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 = inner<std::decay_t<Type>...>;
|
||||
inline static const family_type type = identifier++;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_CORE_FAMILY_HPP
|
||||
#endif
|
||||
|
||||
18
src/entt/core/fwd.hpp
Normal file
18
src/entt/core/fwd.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef ENTT_CORE_FWD_HPP
|
||||
#define ENTT_CORE_FWD_HPP
|
||||
|
||||
|
||||
#include "../config/config.h"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/*! @brief Alias declaration for type identifiers. */
|
||||
using id_type = ENTT_ID_TYPE;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -3,7 +3,9 @@
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
@@ -24,6 +26,7 @@ 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;
|
||||
};
|
||||
@@ -31,6 +34,7 @@ struct fnv1a_traits<std::uint32_t> {
|
||||
|
||||
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;
|
||||
};
|
||||
@@ -41,7 +45,7 @@ struct fnv1a_traits<std::uint64_t> {
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond TURN_OFF_DOXYGEN
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
|
||||
@@ -58,7 +62,7 @@ struct fnv1a_traits<std::uint64_t> {
|
||||
*/
|
||||
template<typename Char>
|
||||
class basic_hashed_string {
|
||||
using traits_type = internal::fnv1a_traits<ENTT_ID_TYPE>;
|
||||
using traits_type = internal::fnv1a_traits<id_type>;
|
||||
|
||||
struct const_wrapper {
|
||||
// non-explicit constructor on purpose
|
||||
@@ -67,15 +71,21 @@ class basic_hashed_string {
|
||||
};
|
||||
|
||||
// Fowler–Noll–Vo hash function v. 1a - the good
|
||||
static constexpr ENTT_ID_TYPE helper(ENTT_ID_TYPE partial, const Char *curr) ENTT_NOEXCEPT {
|
||||
return curr[0] == 0 ? partial : helper((partial^curr[0])*traits_type::prime, curr+1);
|
||||
[[nodiscard]] static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT {
|
||||
auto value = traits_type::offset;
|
||||
|
||||
while(*curr != 0) {
|
||||
value = (value ^ static_cast<traits_type::type>(*(curr++))) * traits_type::prime;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Character type. */
|
||||
using value_type = Char;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using hash_type = ENTT_ID_TYPE;
|
||||
using hash_type = id_type;
|
||||
|
||||
/**
|
||||
* @brief Returns directly the numeric representation of a string.
|
||||
@@ -93,8 +103,8 @@ public:
|
||||
* @return The numeric representation of the string.
|
||||
*/
|
||||
template<std::size_t N>
|
||||
static constexpr hash_type to_value(const value_type (&str)[N]) ENTT_NOEXCEPT {
|
||||
return helper(traits_type::offset, str);
|
||||
[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT {
|
||||
return helper(str);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,8 +112,8 @@ public:
|
||||
* @param wrapper Helps achieving the purpose by relying on overloading.
|
||||
* @return The numeric representation of the string.
|
||||
*/
|
||||
static hash_type to_value(const_wrapper wrapper) ENTT_NOEXCEPT {
|
||||
return helper(traits_type::offset, wrapper.str);
|
||||
[[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT {
|
||||
return helper(wrapper.str);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,8 +122,8 @@ public:
|
||||
* @param size Length of the string to hash.
|
||||
* @return The numeric representation of the string.
|
||||
*/
|
||||
static hash_type to_value(const value_type *str, std::size_t size) ENTT_NOEXCEPT {
|
||||
ENTT_ID_TYPE partial{traits_type::offset};
|
||||
[[nodiscard]] static hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT {
|
||||
id_type partial{traits_type::offset};
|
||||
while(size--) { partial = (partial^(str++)[0])*traits_type::prime; }
|
||||
return partial;
|
||||
}
|
||||
@@ -139,7 +149,7 @@ public:
|
||||
*/
|
||||
template<std::size_t N>
|
||||
constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT
|
||||
: str{curr}, hash{helper(traits_type::offset, curr)}
|
||||
: str{curr}, hash{helper(curr)}
|
||||
{}
|
||||
|
||||
/**
|
||||
@@ -148,14 +158,14 @@ public:
|
||||
* @param wrapper Helps achieving the purpose by relying on overloading.
|
||||
*/
|
||||
explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT
|
||||
: str{wrapper.str}, hash{helper(traits_type::offset, wrapper.str)}
|
||||
: 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.
|
||||
*/
|
||||
constexpr const value_type * data() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr const value_type * data() const ENTT_NOEXCEPT {
|
||||
return str;
|
||||
}
|
||||
|
||||
@@ -163,25 +173,25 @@ public:
|
||||
* @brief Returns the numeric representation of a hashed string.
|
||||
* @return The numeric representation of the instance.
|
||||
*/
|
||||
constexpr hash_type value() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT {
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the human-readable representation of a hashed string.
|
||||
* @return The string used to initialize the instance.
|
||||
*/
|
||||
constexpr operator const value_type *() const ENTT_NOEXCEPT { return str; }
|
||||
/*! @copydoc data */
|
||||
[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { return data(); }
|
||||
|
||||
/*! @copydoc value */
|
||||
constexpr operator hash_type() const ENTT_NOEXCEPT { return hash; }
|
||||
/**
|
||||
* @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(); }
|
||||
|
||||
/**
|
||||
* @brief Compares two hashed strings.
|
||||
* @param other Hashed string with which to compare.
|
||||
* @return True if the two hashed strings are identical, false otherwise.
|
||||
*/
|
||||
constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT {
|
||||
return hash == other.hash;
|
||||
}
|
||||
|
||||
@@ -214,7 +224,7 @@ basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT
|
||||
* @return True if the two hashed strings are identical, false otherwise.
|
||||
*/
|
||||
template<typename Char>
|
||||
constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
@@ -235,7 +245,7 @@ using hashed_wstring = basic_hashed_string<wchar_t>;
|
||||
* @param str The literal without its suffix.
|
||||
* @return A properly initialized hashed string.
|
||||
*/
|
||||
constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT {
|
||||
return entt::hashed_string{str};
|
||||
}
|
||||
|
||||
@@ -245,9 +255,9 @@ constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::si
|
||||
* @param str The literal without its suffix.
|
||||
* @return A properly initialized hashed wstring.
|
||||
*/
|
||||
constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT {
|
||||
return entt::hashed_wstring{str};
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_CORE_HASHED_STRING_HPP
|
||||
#endif
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
|
||||
|
||||
#include <tuple>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
@@ -43,22 +45,22 @@ class identifier {
|
||||
using tuple_type = std::tuple<std::decay_t<Types>...>;
|
||||
|
||||
template<typename Type, std::size_t... Indexes>
|
||||
static constexpr ENTT_ID_TYPE get(std::index_sequence<Indexes...>) ENTT_NOEXCEPT {
|
||||
static_assert(std::disjunction_v<std::is_same<Type, Types>...>);
|
||||
return (0 + ... + (std::is_same_v<Type, std::tuple_element_t<Indexes, tuple_type>> ? ENTT_ID_TYPE(Indexes) : ENTT_ID_TYPE{}));
|
||||
[[nodiscard]] static constexpr id_type get(std::index_sequence<Indexes...>) {
|
||||
static_assert(std::disjunction_v<std::is_same<Type, Types>...>, "Invalid type");
|
||||
return (0 + ... + (std::is_same_v<Type, std::tuple_element_t<Indexes, tuple_type>> ? id_type(Indexes) : id_type{}));
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using identifier_type = ENTT_ID_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::make_index_sequence<sizeof...(Types)>{});
|
||||
static constexpr identifier_type type = get<std::decay_t<Type>>(std::index_sequence_for<Types...>{});
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_CORE_IDENT_HPP
|
||||
#endif
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
#define ENTT_CORE_MONOSTATE_HPP
|
||||
|
||||
|
||||
#include <cassert>
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
@@ -20,7 +20,7 @@ namespace entt {
|
||||
* both during an assignment and when they try to read back their data.
|
||||
* Otherwise, they can incur in unexpected results.
|
||||
*/
|
||||
template<ENTT_ID_TYPE>
|
||||
template<id_type>
|
||||
struct monostate {
|
||||
/**
|
||||
* @brief Assigns a value of a specific type to a given key.
|
||||
@@ -52,11 +52,11 @@ private:
|
||||
* @brief Helper variable template.
|
||||
* @tparam Value Value used to differentiate between different variables.
|
||||
*/
|
||||
template<ENTT_ID_TYPE Value>
|
||||
template<id_type Value>
|
||||
inline monostate<Value> monostate_v = {};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_CORE_MONOSTATE_HPP
|
||||
#endif
|
||||
|
||||
144
src/entt/core/type_info.hpp
Normal file
144
src/entt/core/type_info.hpp
Normal file
@@ -0,0 +1,144 @@
|
||||
#ifndef ENTT_CORE_TYPE_INFO_HPP
|
||||
#define ENTT_CORE_TYPE_INFO_HPP
|
||||
|
||||
|
||||
#include <string_view>
|
||||
#include "../config/config.h"
|
||||
#include "../core/attribute.h"
|
||||
#include "hashed_string.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
|
||||
namespace internal {
|
||||
|
||||
|
||||
struct ENTT_API type_index {
|
||||
[[nodiscard]] static id_type next() ENTT_NOEXCEPT {
|
||||
static ENTT_MAYBE_ATOMIC(id_type) value{};
|
||||
return value++;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr auto 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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Type index.
|
||||
* @tparam Type Type for which to generate a sequential identifier.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
struct ENTT_API type_index {
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type is
|
||||
* indexable, false otherwise.
|
||||
* @tparam Type Potentially indexable type.
|
||||
*/
|
||||
template<typename, typename = void>
|
||||
struct has_type_index: std::false_type {};
|
||||
|
||||
|
||||
/*! @brief has_type_index */
|
||||
template<typename Type>
|
||||
struct has_type_index<Type, std::void_t<decltype(type_index<Type>::value())>>: std::true_type {};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type Potentially indexable type.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr bool has_type_index_v = has_type_index<Type>::value;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Type info.
|
||||
* @tparam Type Type for which to generate information.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
struct type_info {
|
||||
/**
|
||||
* @brief Returns the numeric representation of a given type.
|
||||
* @return The numeric representation of the given type.
|
||||
*/
|
||||
#if defined ENTT_PRETTY_FUNCTION_CONSTEXPR
|
||||
[[nodiscard]] static constexpr id_type id() ENTT_NOEXCEPT {
|
||||
constexpr auto value = hashed_string::value(ENTT_PRETTY_FUNCTION);
|
||||
return value;
|
||||
}
|
||||
#elif defined ENTT_PRETTY_FUNCTION
|
||||
[[nodiscard]] static id_type id() ENTT_NOEXCEPT {
|
||||
static const auto value = hashed_string::value(ENTT_PRETTY_FUNCTION);
|
||||
return value;
|
||||
}
|
||||
#else
|
||||
[[nodiscard]] static id_type id() ENTT_NOEXCEPT {
|
||||
return type_index<Type>::value();
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Returns the name of a given type.
|
||||
* @return The name of the given type.
|
||||
*/
|
||||
#if defined ENTT_PRETTY_FUNCTION_CONSTEXPR
|
||||
[[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT {
|
||||
constexpr auto value = internal::type_name<Type>();
|
||||
return value;
|
||||
}
|
||||
#elif defined ENTT_PRETTY_FUNCTION
|
||||
[[nodiscard]] static std::string_view name() ENTT_NOEXCEPT {
|
||||
static const auto value = internal::type_name<Type>();
|
||||
return value;
|
||||
}
|
||||
#else
|
||||
[[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT {
|
||||
return internal::type_name<Type>();
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -2,14 +2,76 @@
|
||||
#define ENTT_CORE_TYPE_TRAITS_HPP
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "../core/hashed_string.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @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_t = 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_v = 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 ease 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 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 A class to use to push around lists of types, nothing more. */
|
||||
template<typename...>
|
||||
struct type_list {};
|
||||
@@ -35,7 +97,7 @@ struct type_list_size<type_list<Type...>>
|
||||
* @tparam List Type list.
|
||||
*/
|
||||
template<class List>
|
||||
constexpr auto type_list_size_v = type_list_size<List>::value;
|
||||
inline constexpr auto type_list_size_v = type_list_size<List>::value;
|
||||
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
@@ -120,158 +182,81 @@ template<typename Type>
|
||||
using type_list_unique_t = typename type_list_unique<Type>::type;
|
||||
|
||||
|
||||
/*! @brief Traits class used mainly to push things across boundaries. */
|
||||
template<typename>
|
||||
struct named_type_traits;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Specialization used to get rid of constness.
|
||||
* @tparam Type Named type.
|
||||
* @brief Provides the member constant `value` to true if a given type is
|
||||
* equality comparable, false otherwise.
|
||||
* @tparam Type Potentially equality comparable type.
|
||||
*/
|
||||
template<typename Type, typename = std::void_t<>>
|
||||
struct is_equality_comparable: std::false_type {};
|
||||
|
||||
|
||||
/*! @copydoc is_equality_comparable */
|
||||
template<typename Type>
|
||||
struct named_type_traits<const Type>
|
||||
: named_type_traits<Type>
|
||||
struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Type Potentially named type.
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type Potentially equality comparable type.
|
||||
*/
|
||||
template<typename Type>
|
||||
using named_type_traits_t = typename named_type_traits<Type>::type;
|
||||
template<class Type>
|
||||
inline constexpr auto is_equality_comparable_v = is_equality_comparable<Type>::value;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type has a
|
||||
* name. In all other cases, `value` is false.
|
||||
* @brief Provides the member constant `value` to true if a given type is empty
|
||||
* and the empty type optimization is enabled, false otherwise.
|
||||
* @tparam Type Potential empty type.
|
||||
*/
|
||||
template<typename, typename = std::void_t<>>
|
||||
struct is_named_type: std::false_type {};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type has a
|
||||
* name. In all other cases, `value` is false.
|
||||
* @tparam Type Potentially named type.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct is_named_type<Type, std::void_t<named_type_traits_t<std::decay_t<Type>>>>: std::true_type {};
|
||||
template<typename Type, typename = void>
|
||||
struct is_eto_eligible
|
||||
: ENTT_IS_EMPTY(Type)
|
||||
{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type Potentially named type.
|
||||
* @tparam Type Potential empty type.
|
||||
*/
|
||||
template<class Type>
|
||||
constexpr auto is_named_type_v = is_named_type<Type>::value;
|
||||
template<typename Type>
|
||||
inline constexpr auto is_eto_eligible_v = is_eto_eligible<Type>::value;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Defines an enum class to use for opaque identifiers and a dedicate
|
||||
* `to_integer` function to convert the identifiers to their underlying type.
|
||||
* @param clazz The name to use for the enum class.
|
||||
* @param type The underlying type for the enum class.
|
||||
* @brief Extracts the class of a non-static member object or function.
|
||||
* @tparam Member A pointer to a non-static member object or function.
|
||||
*/
|
||||
#define ENTT_OPAQUE_TYPE(clazz, type)\
|
||||
enum class clazz: type {};\
|
||||
constexpr auto to_integer(const clazz id) ENTT_NOEXCEPT {\
|
||||
return std::underlying_type_t<clazz>(id);\
|
||||
}
|
||||
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;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Utility macro to deal with an issue of MSVC.
|
||||
*
|
||||
* See _msvc-doesnt-expand-va-args-correctly_ on SO for all the details.
|
||||
*
|
||||
* @param args Argument to expand.
|
||||
*/
|
||||
#define ENTT_EXPAND(args) args
|
||||
|
||||
|
||||
/**
|
||||
* @brief Makes an already existing type a named type.
|
||||
*
|
||||
* The current definition contains a workaround for Clang 6 because it fails to
|
||||
* deduce correctly the type to use to specialize the class template.<br/>
|
||||
* With a compiler that fully supports C++17 and works fine with deduction
|
||||
* guides, the following should be fine instead:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* std::integral_constant<ENTT_ID_TYPE, entt::basic_hashed_string{#type}>
|
||||
* @endcode
|
||||
*
|
||||
* In order to support even sligthly older compilers, I prefer to stick to the
|
||||
* implementation below.
|
||||
*
|
||||
* @param type Type to assign a name to.
|
||||
*/
|
||||
#define ENTT_NAMED_TYPE(type)\
|
||||
template<>\
|
||||
struct entt::named_type_traits<type>\
|
||||
: std::integral_constant<ENTT_ID_TYPE, entt::basic_hashed_string<std::remove_cv_t<std::remove_pointer_t<std::decay_t<decltype(#type)>>>>{#type}>\
|
||||
{\
|
||||
static_assert(std::is_same_v<std::decay_t<type>, type>);\
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Defines a named type (to use for structs).
|
||||
* @param clazz Name of the type to define.
|
||||
* @param body Body of the type to define.
|
||||
*/
|
||||
#define ENTT_NAMED_STRUCT_ONLY(clazz, body)\
|
||||
struct clazz body;\
|
||||
ENTT_NAMED_TYPE(clazz)
|
||||
|
||||
|
||||
/**
|
||||
* @brief Defines a named type (to use for structs).
|
||||
* @param ns Namespace where to define the named type.
|
||||
* @param clazz Name of the type to define.
|
||||
* @param body Body of the type to define.
|
||||
*/
|
||||
#define ENTT_NAMED_STRUCT_WITH_NAMESPACE(ns, clazz, body)\
|
||||
namespace ns { struct clazz body; }\
|
||||
ENTT_NAMED_TYPE(ns::clazz)
|
||||
|
||||
|
||||
/*! @brief Utility function to simulate macro overloading. */
|
||||
#define ENTT_NAMED_STRUCT_OVERLOAD(_1, _2, _3, FUNC, ...) FUNC
|
||||
/*! @brief Defines a named type (to use for structs). */
|
||||
#define ENTT_NAMED_STRUCT(...) ENTT_EXPAND(ENTT_NAMED_STRUCT_OVERLOAD(__VA_ARGS__, ENTT_NAMED_STRUCT_WITH_NAMESPACE, ENTT_NAMED_STRUCT_ONLY,)(__VA_ARGS__))
|
||||
|
||||
|
||||
/**
|
||||
* @brief Defines a named type (to use for classes).
|
||||
* @param clazz Name of the type to define.
|
||||
* @param body Body of the type to define.
|
||||
*/
|
||||
#define ENTT_NAMED_CLASS_ONLY(clazz, body)\
|
||||
class clazz body;\
|
||||
ENTT_NAMED_TYPE(clazz)
|
||||
|
||||
|
||||
/**
|
||||
* @brief Defines a named type (to use for classes).
|
||||
* @param ns Namespace where to define the named type.
|
||||
* @param clazz Name of the type to define.
|
||||
* @param body Body of the type to define.
|
||||
*/
|
||||
#define ENTT_NAMED_CLASS_WITH_NAMESPACE(ns, clazz, body)\
|
||||
namespace ns { class clazz body; }\
|
||||
ENTT_NAMED_TYPE(ns::clazz)
|
||||
|
||||
|
||||
/*! @brief Utility function to simulate macro overloading. */
|
||||
#define ENTT_NAMED_CLASS_MACRO(_1, _2, _3, FUNC, ...) FUNC
|
||||
/*! @brief Defines a named type (to use for classes). */
|
||||
#define ENTT_NAMED_CLASS(...) ENTT_EXPAND(ENTT_NAMED_CLASS_MACRO(__VA_ARGS__, ENTT_NAMED_CLASS_WITH_NAMESPACE, ENTT_NAMED_CLASS_ONLY,)(__VA_ARGS__))
|
||||
|
||||
|
||||
#endif // ENTT_CORE_TYPE_TRAITS_HPP
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define ENTT_CORE_UTILITY_HPP
|
||||
|
||||
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
|
||||
|
||||
@@ -17,34 +18,88 @@ struct identity {
|
||||
* @return The submitted value as-is.
|
||||
*/
|
||||
template<class Type>
|
||||
constexpr Type && operator()(Type &&value) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr Type && operator()(Type &&value) const ENTT_NOEXCEPT {
|
||||
return std::forward<Type>(value);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Constant utility to disambiguate overloaded member functions.
|
||||
* @tparam Type Function type of the desired overload.
|
||||
* @tparam Class Type of class to which the member functions belong.
|
||||
* @param member A valid pointer to a member function.
|
||||
* @return Pointer to the member function.
|
||||
* @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>
|
||||
constexpr auto overload(Type Class:: *member) ENTT_NOEXCEPT { return member; }
|
||||
[[nodiscard]] constexpr auto overload(Type Class:: *member) ENTT_NOEXCEPT { return member; }
|
||||
|
||||
|
||||
/**
|
||||
* @brief Constant utility to disambiguate overloaded functions.
|
||||
* @tparam Type Function type of the desired overload.
|
||||
* @tparam Func Function type of the desired overload.
|
||||
* @param func A valid pointer to a function.
|
||||
* @return Pointer to the function.
|
||||
*/
|
||||
template<typename Type>
|
||||
constexpr auto overload(Type *func) ENTT_NOEXCEPT { return func; }
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_CORE_UTILITY_HPP
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#define ENTT_ENTITY_ACTOR_HPP
|
||||
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
@@ -25,16 +24,31 @@ namespace entt {
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
struct basic_actor {
|
||||
struct [[deprecated("Consider using the handle class instead")]] basic_actor {
|
||||
/*! @brief Type of registry used internally. */
|
||||
using registry_type = basic_registry<Entity>;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
|
||||
basic_actor() ENTT_NOEXCEPT
|
||||
: entt{entt::null}, reg{nullptr}
|
||||
: entt{null}, reg{nullptr}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*
|
||||
* After actor move construction, instances that have been moved from are
|
||||
* placed in a valid but unspecified state. It's highly discouraged to
|
||||
* continue using them.
|
||||
*
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_actor(basic_actor &&other) ENTT_NOEXCEPT
|
||||
: entt{other.entt}, reg{other.reg}
|
||||
{
|
||||
other.entt = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs an actor from a given registry.
|
||||
* @param ref An instance of the registry class.
|
||||
@@ -48,7 +62,7 @@ struct basic_actor {
|
||||
* @param entity A valid entity identifier.
|
||||
* @param ref An instance of the registry class.
|
||||
*/
|
||||
explicit basic_actor(entity_type entity, registry_type &ref)
|
||||
explicit basic_actor(entity_type entity, registry_type &ref) ENTT_NOEXCEPT
|
||||
: entt{entity}, reg{&ref}
|
||||
{
|
||||
ENTT_ASSERT(ref.valid(entity));
|
||||
@@ -61,21 +75,6 @@ struct basic_actor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*
|
||||
* After actor move construction, instances that have been moved from are
|
||||
* placed in a valid but unspecified state. It's highly discouraged to
|
||||
* continue using them.
|
||||
*
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_actor(basic_actor &&other)
|
||||
: entt{other.entt}, reg{other.reg}
|
||||
{
|
||||
other.entt = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
*
|
||||
@@ -86,7 +85,7 @@ struct basic_actor {
|
||||
* @param other The instance to move from.
|
||||
* @return This actor.
|
||||
*/
|
||||
basic_actor & operator=(basic_actor &&other) {
|
||||
basic_actor & operator=(basic_actor &&other) ENTT_NOEXCEPT {
|
||||
if(this != &other) {
|
||||
auto tmp{std::move(other)};
|
||||
std::swap(reg, tmp.reg);
|
||||
@@ -112,7 +111,7 @@ struct basic_actor {
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
decltype(auto) assign(Args &&... args) {
|
||||
return reg->template assign_or_replace<Component>(entt, std::forward<Args>(args)...);
|
||||
return reg->template emplace_or_replace<Component>(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,8 +129,8 @@ struct basic_actor {
|
||||
* @return True if the actor has all the components, false otherwise.
|
||||
*/
|
||||
template<typename... Component>
|
||||
bool has() const ENTT_NOEXCEPT {
|
||||
return (reg->template has<Component>(entt) && ...);
|
||||
[[nodiscard]] bool has() const {
|
||||
return reg->template has<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,13 +139,13 @@ struct basic_actor {
|
||||
* @return References to the components owned by the actor.
|
||||
*/
|
||||
template<typename... Component>
|
||||
decltype(auto) get() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] decltype(auto) get() const {
|
||||
return std::as_const(*reg).template get<Component...>(entt);
|
||||
}
|
||||
|
||||
/*! @copydoc get */
|
||||
template<typename... Component>
|
||||
decltype(auto) get() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] decltype(auto) get() {
|
||||
return reg->template get<Component...>(entt);
|
||||
}
|
||||
|
||||
@@ -156,13 +155,13 @@ struct basic_actor {
|
||||
* @return Pointers to the components owned by the actor.
|
||||
*/
|
||||
template<typename... Component>
|
||||
auto try_get() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] auto try_get() const {
|
||||
return std::as_const(*reg).template try_get<Component...>(entt);
|
||||
}
|
||||
|
||||
/*! @copydoc try_get */
|
||||
template<typename... Component>
|
||||
auto try_get() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] auto try_get() {
|
||||
return reg->template try_get<Component...>(entt);
|
||||
}
|
||||
|
||||
@@ -170,12 +169,12 @@ struct basic_actor {
|
||||
* @brief Returns a reference to the underlying registry.
|
||||
* @return A reference to the underlying registry.
|
||||
*/
|
||||
const registry_type & backend() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] const registry_type & backend() const ENTT_NOEXCEPT {
|
||||
return *reg;
|
||||
}
|
||||
|
||||
/*! @copydoc backend */
|
||||
registry_type & backend() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] registry_type & backend() ENTT_NOEXCEPT {
|
||||
return const_cast<registry_type &>(std::as_const(*this).backend());
|
||||
}
|
||||
|
||||
@@ -183,7 +182,7 @@ struct basic_actor {
|
||||
* @brief Returns the entity associated with an actor.
|
||||
* @return The entity associated with the actor.
|
||||
*/
|
||||
entity_type entity() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] entity_type entity() const ENTT_NOEXCEPT {
|
||||
return entt;
|
||||
}
|
||||
|
||||
@@ -191,7 +190,7 @@ struct basic_actor {
|
||||
* @brief Checks if an actor refers to a valid entity or not.
|
||||
* @return True if the actor refers to a valid entity, false otherwise.
|
||||
*/
|
||||
explicit operator bool() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] explicit operator bool() const {
|
||||
return reg && reg->valid(entt);
|
||||
}
|
||||
|
||||
@@ -204,4 +203,4 @@ private:
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_ENTITY_ACTOR_HPP
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define ENTT_ENTITY_ENTITY_HPP
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
@@ -16,10 +17,20 @@ namespace entt {
|
||||
* Primary template isn't defined on purpose. All the specializations give a
|
||||
* compile-time error unless the template parameter is an accepted entity type.
|
||||
*/
|
||||
template<typename>
|
||||
template<typename, typename = void>
|
||||
struct entt_traits;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Entity traits for enumeration types.
|
||||
* @tparam Type The type to check.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct entt_traits<Type, std::enable_if_t<std::is_enum_v<Type>>>
|
||||
: entt_traits<std::underlying_type_t<Type>>
|
||||
{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Entity traits for a 16 bits entity identifier.
|
||||
*
|
||||
@@ -35,14 +46,14 @@ struct entt_traits<std::uint16_t> {
|
||||
/*! @brief Underlying version type. */
|
||||
using version_type = std::uint8_t;
|
||||
/*! @brief Difference type. */
|
||||
using difference_type = std::int32_t;
|
||||
using difference_type = std::int16_t;
|
||||
|
||||
/*! @brief Mask to use to get the entity number out of an identifier. */
|
||||
static constexpr std::uint16_t entity_mask = 0xFFF;
|
||||
static constexpr entity_type entity_mask = 0xFFF;
|
||||
/*! @brief Mask to use to get the version out of an identifier. */
|
||||
static constexpr std::uint16_t version_mask = 0xF;
|
||||
static constexpr entity_type version_mask = 0xF;
|
||||
/*! @brief Extent of the entity number within an identifier. */
|
||||
static constexpr auto entity_shift = 12;
|
||||
static constexpr std::size_t entity_shift = 12u;
|
||||
};
|
||||
|
||||
|
||||
@@ -61,14 +72,14 @@ struct entt_traits<std::uint32_t> {
|
||||
/*! @brief Underlying version type. */
|
||||
using version_type = std::uint16_t;
|
||||
/*! @brief Difference type. */
|
||||
using difference_type = std::int64_t;
|
||||
using difference_type = std::int32_t;
|
||||
|
||||
/*! @brief Mask to use to get the entity number out of an identifier. */
|
||||
static constexpr std::uint32_t entity_mask = 0xFFFFF;
|
||||
static constexpr entity_type entity_mask = 0xFFFFF;
|
||||
/*! @brief Mask to use to get the version out of an identifier. */
|
||||
static constexpr std::uint32_t version_mask = 0xFFF;
|
||||
static constexpr entity_type version_mask = 0xFFF;
|
||||
/*! @brief Extent of the entity number within an identifier. */
|
||||
static constexpr auto entity_shift = 20;
|
||||
static constexpr std::size_t entity_shift = 20u;
|
||||
};
|
||||
|
||||
|
||||
@@ -90,71 +101,107 @@ struct entt_traits<std::uint64_t> {
|
||||
using difference_type = std::int64_t;
|
||||
|
||||
/*! @brief Mask to use to get the entity number out of an identifier. */
|
||||
static constexpr std::uint64_t entity_mask = 0xFFFFFFFF;
|
||||
static constexpr entity_type entity_mask = 0xFFFFFFFF;
|
||||
/*! @brief Mask to use to get the version out of an identifier. */
|
||||
static constexpr std::uint64_t version_mask = 0xFFFFFFFF;
|
||||
static constexpr entity_type version_mask = 0xFFFFFFFF;
|
||||
/*! @brief Extent of the entity number within an identifier. */
|
||||
static constexpr auto entity_shift = 32;
|
||||
static constexpr std::size_t entity_shift = 32u;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
* @brief Converts an entity type to its underlying type.
|
||||
* @tparam Entity The value type.
|
||||
* @param entity The value to convert.
|
||||
* @return The integral representation of the given value.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr auto to_integral(const Entity entity) ENTT_NOEXCEPT {
|
||||
return static_cast<typename entt_traits<Entity>::entity_type>(entity);
|
||||
}
|
||||
|
||||
|
||||
namespace internal {
|
||||
|
||||
|
||||
class null {
|
||||
/*! @brief Null object for all entity identifiers. */
|
||||
struct null_t {
|
||||
/**
|
||||
* @brief Converts the null object to identifiers of any type.
|
||||
* @tparam Entity Type of entity identifier.
|
||||
* @return The null representation for the given identifier.
|
||||
*/
|
||||
template<typename Entity>
|
||||
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
|
||||
|
||||
public:
|
||||
template<typename Entity>
|
||||
constexpr operator Entity() const ENTT_NOEXCEPT {
|
||||
return Entity{traits_type<Entity>::entity_mask};
|
||||
[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT {
|
||||
return Entity{entt_traits<Entity>::entity_mask};
|
||||
}
|
||||
|
||||
constexpr bool operator==(null) const ENTT_NOEXCEPT {
|
||||
/**
|
||||
* @brief Compares two null objects.
|
||||
* @return True in all cases.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator==(null_t) const ENTT_NOEXCEPT {
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(null) const ENTT_NOEXCEPT {
|
||||
/**
|
||||
* @brief Compares two null objects.
|
||||
* @return False in all cases.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator!=(null_t) const ENTT_NOEXCEPT {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a null object and an entity identifier of any type.
|
||||
* @tparam Entity Type of entity identifier.
|
||||
* @param entity Entity identifier with which to compare.
|
||||
* @return False if the two elements differ, true otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
|
||||
return (to_integer(entity) & traits_type<Entity>::entity_mask) == to_integer(static_cast<Entity>(*this));
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
|
||||
return (to_integral(entity) & entt_traits<Entity>::entity_mask) == to_integral(static_cast<Entity>(*this));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a null object and an entity identifier of any type.
|
||||
* @tparam Entity Type of entity identifier.
|
||||
* @param entity Entity identifier with which to compare.
|
||||
* @return True if the two elements differ, false otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
|
||||
return !(entity == *this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Compares a null object and an entity identifier of any type.
|
||||
* @tparam Entity Type of entity identifier.
|
||||
* @param entity 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>
|
||||
constexpr bool operator==(const Entity entity, null other) ENTT_NOEXCEPT {
|
||||
return other == entity;
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity, null_t other) ENTT_NOEXCEPT {
|
||||
return other.operator==(entity);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Compares a null object and an entity identifier of any type.
|
||||
* @tparam Entity Type of entity identifier.
|
||||
* @param entity 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>
|
||||
constexpr bool operator!=(const Entity entity, null other) ENTT_NOEXCEPT {
|
||||
return other != entity;
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity, null_t other) ENTT_NOEXCEPT {
|
||||
return !(other == entity);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond TURN_OFF_DOXYGEN
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
|
||||
@@ -165,10 +212,10 @@ constexpr bool operator!=(const Entity entity, null other) ENTT_NOEXCEPT {
|
||||
* any allowed type. Similarly, there exist comparision operators between the
|
||||
* null entity and any other entity identifier.
|
||||
*/
|
||||
constexpr auto null = internal::null{};
|
||||
inline constexpr null_t null{};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_ENTITY_ENTITY_HPP
|
||||
#endif
|
||||
|
||||
@@ -2,90 +2,100 @@
|
||||
#define ENTT_ENTITY_FWD_HPP
|
||||
|
||||
|
||||
#include <cstdint>
|
||||
#include "../config/config.h"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../core/fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @class basic_registry */
|
||||
|
||||
template <typename>
|
||||
class basic_registry;
|
||||
|
||||
/*! @class basic_view */
|
||||
template<typename, typename...>
|
||||
|
||||
template<typename...>
|
||||
class basic_view;
|
||||
|
||||
/*! @class basic_runtime_view */
|
||||
|
||||
template<typename>
|
||||
class basic_runtime_view;
|
||||
|
||||
/*! @class basic_group */
|
||||
|
||||
template<typename...>
|
||||
class basic_group;
|
||||
|
||||
/*! @class basic_observer */
|
||||
|
||||
template<typename>
|
||||
class basic_observer;
|
||||
|
||||
/*! @class basic_actor */
|
||||
|
||||
template <typename>
|
||||
struct basic_actor;
|
||||
|
||||
/*! @class basic_prototype */
|
||||
template<typename>
|
||||
class basic_prototype;
|
||||
|
||||
/*! @class basic_snapshot */
|
||||
template<typename>
|
||||
struct basic_handle;
|
||||
|
||||
|
||||
template<typename>
|
||||
class basic_snapshot;
|
||||
|
||||
/*! @class basic_snapshot_loader */
|
||||
|
||||
template<typename>
|
||||
class basic_snapshot_loader;
|
||||
|
||||
/*! @class basic_continuous_loader */
|
||||
|
||||
template<typename>
|
||||
class basic_continuous_loader;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
ENTT_OPAQUE_TYPE(entity, ENTT_ID_TYPE)
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
ENTT_OPAQUE_TYPE(component, ENTT_ID_TYPE)
|
||||
/*! @brief Default entity identifier. */
|
||||
enum class entity: id_type {};
|
||||
|
||||
|
||||
/*! @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 actor = basic_actor<entity>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using prototype = basic_prototype<entity>;
|
||||
using actor [[deprecated("Consider using the handle class instead")]] = basic_actor<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. */
|
||||
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 Component Types of components iterated by the view.
|
||||
* @tparam Types Types of components iterated by the view.
|
||||
*/
|
||||
template<typename... Types>
|
||||
using view = basic_view<entity, Types...>;
|
||||
|
||||
|
||||
/*! @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 Types Types of components iterated by the group.
|
||||
@@ -97,4 +107,4 @@ using group = basic_group<entity, Types...>;
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_ENTITY_FWD_HPP
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
280
src/entt/entity/handle.hpp
Normal file
280
src/entt/entity/handle.hpp
Normal file
@@ -0,0 +1,280 @@
|
||||
#ifndef ENTT_ENTITY_HANDLE_HPP
|
||||
#define ENTT_ENTITY_HANDLE_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).
|
||||
*/
|
||||
template<typename Entity>
|
||||
struct basic_handle {
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = std::remove_const_t<Entity>;
|
||||
|
||||
/*! @brief Type of registry accepted by the handle. */
|
||||
using registry_type = std::conditional_t<
|
||||
std::is_const_v<Entity>,
|
||||
const basic_registry<entity_type>,
|
||||
basic_registry<entity_type>
|
||||
>;
|
||||
|
||||
/**
|
||||
* @brief Constructs a handle from a given registry and entity.
|
||||
* @param ref An instance of the registry class.
|
||||
* @param value An entity identifier.
|
||||
*/
|
||||
basic_handle(registry_type &ref, entity_type value = null) ENTT_NOEXCEPT
|
||||
: reg{&ref}, entt{value}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Assigns an entity to a handle.
|
||||
* @param value An entity identifier.
|
||||
* @return This handle.
|
||||
*/
|
||||
basic_handle & operator=(const entity_type value) ENTT_NOEXCEPT {
|
||||
entt = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns the null object to a handle.
|
||||
* @return This handle.
|
||||
*/
|
||||
basic_handle & operator=(null_t) ENTT_NOEXCEPT {
|
||||
return (*this = static_cast<entity_type>(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs a const handle from a non-const one.
|
||||
* @return A const handle referring to the same entity.
|
||||
*/
|
||||
[[nodiscard]] operator basic_handle<const entity_type>() const ENTT_NOEXCEPT {
|
||||
return {*reg, entt};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts a handle to its underlying entity.
|
||||
* @return An entity identifier.
|
||||
*/
|
||||
[[nodiscard]] operator entity_type() const ENTT_NOEXCEPT {
|
||||
return entity();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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]] explicit operator bool() const {
|
||||
return reg->valid(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the underlying registry.
|
||||
* @return A reference to the underlying registry.
|
||||
*/
|
||||
[[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 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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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.
|
||||
*/
|
||||
template<typename... Components>
|
||||
void remove() const {
|
||||
reg->template remove<Components...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the given components from a handle.
|
||||
* @sa basic_registry::remove_if_exists
|
||||
* @tparam Component Types of components to remove.
|
||||
* @return The number of components actually removed.
|
||||
*/
|
||||
template<typename... Components>
|
||||
decltype(auto) remove_if_exists() const {
|
||||
return reg->template remove_if_exists<Components...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes all the components from a handle and makes it orphaned.
|
||||
* @sa basic_registry::remove_all
|
||||
*/
|
||||
void remove_all() const {
|
||||
reg->remove_all(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle has all the given components.
|
||||
* @sa basic_registry::has
|
||||
* @tparam Component Components for which to perform the check.
|
||||
* @return True if the handle has all the components, false otherwise.
|
||||
*/
|
||||
template<typename... Components>
|
||||
[[nodiscard]] decltype(auto) has() const {
|
||||
return reg->template has<Components...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle has at least one of the given components.
|
||||
* @sa basic_registry::any
|
||||
* @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... Components>
|
||||
[[nodiscard]] decltype(auto) any() const {
|
||||
return reg->template any<Components...>(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... Components>
|
||||
[[nodiscard]] decltype(auto) get() const {
|
||||
return reg->template get<Components...>(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 {
|
||||
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... Components>
|
||||
[[nodiscard]] decltype(auto) try_get() const {
|
||||
return reg->template try_get<Components...>(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 types for its components.
|
||||
* @sa basic_registry::visit
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void visit(Func &&func) const {
|
||||
reg->visit(entt, std::forward<Func>(func));
|
||||
}
|
||||
|
||||
private:
|
||||
registry_type *reg;
|
||||
entity_type entt;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @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>;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -4,8 +4,10 @@
|
||||
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "../signal/sigh.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../signal/delegate.hpp"
|
||||
#include "registry.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
@@ -19,7 +21,7 @@ namespace entt {
|
||||
template<bool Const, typename Entity>
|
||||
struct as_view {
|
||||
/*! @brief Type of registry to convert. */
|
||||
using registry_type = std::conditional_t<Const, const entt::basic_registry<Entity>, entt::basic_registry<Entity>>;
|
||||
using registry_type = std::conditional_t<Const, const basic_registry<Entity>, basic_registry<Entity>>;
|
||||
|
||||
/**
|
||||
* @brief Constructs a converter for a given registry.
|
||||
@@ -29,12 +31,13 @@ struct as_view {
|
||||
|
||||
/**
|
||||
* @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... Component>
|
||||
operator entt::basic_view<Entity, Component...>() const {
|
||||
return reg.template view<Component...>();
|
||||
template<typename Exclude, typename... Component>
|
||||
operator basic_view<Entity, Exclude, Component...>() const {
|
||||
return reg.template view<Component...>(Exclude{});
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -67,7 +70,7 @@ as_view(const basic_registry<Entity> &) ENTT_NOEXCEPT -> as_view<true, Entity>;
|
||||
template<bool Const, typename Entity>
|
||||
struct as_group {
|
||||
/*! @brief Type of registry to convert. */
|
||||
using registry_type = std::conditional_t<Const, const entt::basic_registry<Entity>, entt::basic_registry<Entity>>;
|
||||
using registry_type = std::conditional_t<Const, const basic_registry<Entity>, basic_registry<Entity>>;
|
||||
|
||||
/**
|
||||
* @brief Constructs a converter for a given registry.
|
||||
@@ -83,7 +86,7 @@ struct as_group {
|
||||
* @return A newly created group.
|
||||
*/
|
||||
template<typename Exclude, typename Get, typename... Owned>
|
||||
operator entt::basic_group<Entity, Exclude, Get, Owned...>() const {
|
||||
operator basic_group<Entity, Exclude, Get, Owned...>() const {
|
||||
return reg.template group<Owned...>(Get{}, Exclude{});
|
||||
}
|
||||
|
||||
@@ -109,30 +112,38 @@ template<typename Entity>
|
||||
as_group(const basic_registry<Entity> &) ENTT_NOEXCEPT -> as_group<true, Entity>;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Alias template to ease the assignment of tags to entities.
|
||||
*
|
||||
* If used in combination with hashed strings, it simplifies the assignment of
|
||||
* tags to entities and the use of tags in general where a type would be
|
||||
* required otherwise.<br/>
|
||||
* As an example and where the user defined literal for hashed strings hasn't
|
||||
* been changed:
|
||||
* @code{.cpp}
|
||||
* entt::registry registry;
|
||||
* registry.assign<entt::tag<"enemy"_hs>>(entity);
|
||||
* @endcode
|
||||
*
|
||||
* @note
|
||||
* Tags are empty components and therefore candidates for the empty component
|
||||
* optimization.
|
||||
*
|
||||
* @tparam Value The numeric representation of an instance of hashed string.
|
||||
* @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<ENTT_ID_TYPE Value>
|
||||
using tag = std::integral_constant<ENTT_ID_TYPE, Value>;
|
||||
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.
|
||||
* @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 component 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 &component) {
|
||||
return *(reg.template data<Component>() + (&component - reg.template raw<Component>()));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_ENTITY_HELPER_HPP
|
||||
#endif
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "registry.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
@@ -52,7 +52,7 @@ struct basic_collector<> {
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
|
||||
return basic_collector<matcher<matcher<type_list<>, type_list<>>, type_list<NoneOf...>, type_list<AllOf...>>>{};
|
||||
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>>{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,19 +61,24 @@ struct basic_collector<> {
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename AnyOf>
|
||||
static constexpr auto replace() ENTT_NOEXCEPT {
|
||||
return basic_collector<matcher<matcher<type_list<>, type_list<>>, AnyOf>>{};
|
||||
static constexpr auto update() ENTT_NOEXCEPT {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, AnyOf>>{};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Collector.
|
||||
* @copydetails basic_collector<>
|
||||
* @tparam AnyOf Types of components for which changes should be detected.
|
||||
* @tparam Matcher Types of grouping matchers.
|
||||
* @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<matcher<type_list<Reject...>, type_list<Require...>>, Rule...>, 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.
|
||||
@@ -82,8 +87,7 @@ struct basic_collector<matcher<matcher<type_list<Reject...>, type_list<Require..
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
|
||||
using first = matcher<matcher<type_list<Reject...>, type_list<Require...>>, Rule...>;
|
||||
return basic_collector<first, Other..., matcher<matcher<type_list<>, type_list<>>, type_list<NoneOf...>, type_list<AllOf...>>>{};
|
||||
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>, current_type, Other...>{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,9 +96,8 @@ struct basic_collector<matcher<matcher<type_list<Reject...>, type_list<Require..
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename AnyOf>
|
||||
static constexpr auto replace() ENTT_NOEXCEPT {
|
||||
using first = matcher<matcher<type_list<Reject...>, type_list<Require...>>, Rule...>;
|
||||
return basic_collector<first, Other..., matcher<matcher<type_list<>, type_list<>>, AnyOf>>{};
|
||||
static constexpr auto update() ENTT_NOEXCEPT {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, AnyOf>, current_type, Other...>{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,13 +108,14 @@ struct basic_collector<matcher<matcher<type_list<Reject...>, type_list<Require..
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
static constexpr auto where(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
|
||||
return basic_collector<matcher<matcher<type_list<Reject..., NoneOf...>, type_list<Require..., AllOf...>>, Rule...>, Other...>{};
|
||||
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. */
|
||||
constexpr basic_collector<> collector{};
|
||||
inline constexpr basic_collector<> collector{};
|
||||
|
||||
|
||||
/**
|
||||
@@ -126,8 +130,8 @@ constexpr basic_collector<> collector{};
|
||||
* collector:
|
||||
*
|
||||
* * Observing matcher: an observer will return at least all the living entities
|
||||
* for which one or more of the given components have been explicitly
|
||||
* replaced and not yet destroyed.
|
||||
* 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.
|
||||
@@ -156,7 +160,7 @@ constexpr basic_collector<> collector{};
|
||||
* behavior.
|
||||
*
|
||||
* @warning
|
||||
* Lifetime of an observer doesn't necessarily have to overcome the one of the
|
||||
* 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.
|
||||
@@ -171,19 +175,22 @@ class basic_observer {
|
||||
struct matcher_handler;
|
||||
|
||||
template<typename... Reject, typename... Require, typename AnyOf>
|
||||
struct matcher_handler<matcher<matcher<type_list<Reject...>, type_list<Require...>>, 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, const Entity entt, const basic_registry<Entity> ®) {
|
||||
if(reg.template has<Require...>(entt) && !(reg.template has<Reject>(entt) || ...)) {
|
||||
auto *comp = obs.view.try_get(entt);
|
||||
(comp ? *comp : obs.view.construct(entt)) |= (1 << Index);
|
||||
static void maybe_valid_if(basic_observer &obs, const basic_registry<Entity> ®, const Entity entt) {
|
||||
if(reg.template has<Require...>(entt) && !reg.template any<Reject...>(entt)) {
|
||||
if(auto *comp = obs.view.try_get(entt); !comp) {
|
||||
obs.view.emplace(entt);
|
||||
}
|
||||
|
||||
obs.view.get(entt) |= (1 << Index);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void discard_if(basic_observer &obs, const Entity entt) {
|
||||
static void discard_if(basic_observer &obs, const basic_registry<Entity> &, const Entity entt) {
|
||||
if(auto *value = obs.view.try_get(entt); value && !(*value &= (~(1 << Index)))) {
|
||||
obs.view.destroy(entt);
|
||||
obs.view.erase(entt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,34 +198,35 @@ class basic_observer {
|
||||
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_replace<AnyOf>().template connect<&maybe_valid_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_replace<AnyOf>().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<matcher<type_list<Reject...>, type_list<Require...>>, type_list<NoneOf...>, type_list<AllOf...>>> {
|
||||
struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, type_list<NoneOf...>, AllOf...>> {
|
||||
template<std::size_t Index>
|
||||
static void maybe_valid_if(basic_observer &obs, const Entity entt, const basic_registry<Entity> ®) {
|
||||
if(reg.template has<AllOf...>(entt) && !(reg.template has<NoneOf>(entt) || ...)
|
||||
&& reg.template has<Require...>(entt) && !(reg.template has<Reject>(entt) || ...))
|
||||
{
|
||||
auto *comp = obs.view.try_get(entt);
|
||||
(comp ? *comp : obs.view.construct(entt)) |= (1 << Index);
|
||||
static void maybe_valid_if(basic_observer &obs, const basic_registry<Entity> ®, const Entity entt) {
|
||||
if(reg.template has<AllOf..., Require...>(entt) && !reg.template any<NoneOf..., Reject...>(entt)) {
|
||||
if(auto *comp = obs.view.try_get(entt); !comp) {
|
||||
obs.view.emplace(entt);
|
||||
}
|
||||
|
||||
obs.view.get(entt) |= (1 << Index);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void discard_if(basic_observer &obs, const Entity entt) {
|
||||
static void discard_if(basic_observer &obs, const basic_registry<Entity> &, const Entity entt) {
|
||||
if(auto *value = obs.view.try_get(entt); value && !(*value &= (~(1 << Index)))) {
|
||||
obs.view.destroy(entt);
|
||||
obs.view.erase(entt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +257,7 @@ class basic_observer {
|
||||
|
||||
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);
|
||||
static_assert(sizeof...(Matcher) < std::numeric_limits<payload_type>::digits, "Too many matchers");
|
||||
(matcher_handler<Matcher>::template connect<Index>(*this, reg), ...);
|
||||
release = &basic_observer::disconnect<Matcher...>;
|
||||
}
|
||||
@@ -258,12 +266,12 @@ public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename sparse_set<Entity>::size_type;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = typename sparse_set<Entity>::iterator_type;
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = typename sparse_set<Entity>::iterator;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_observer() ENTT_NOEXCEPT
|
||||
basic_observer()
|
||||
: target{}, release{}, view{}
|
||||
{}
|
||||
|
||||
@@ -278,12 +286,12 @@ public:
|
||||
* @param reg A valid reference to a registry.
|
||||
*/
|
||||
template<typename... Matcher>
|
||||
basic_observer(basic_registry<entity_type> ®, basic_collector<Matcher...>) ENTT_NOEXCEPT
|
||||
basic_observer(basic_registry<entity_type> ®, basic_collector<Matcher...>)
|
||||
: target{®},
|
||||
release{},
|
||||
view{}
|
||||
{
|
||||
connect<Matcher...>(reg, std::make_index_sequence<sizeof...(Matcher)>{});
|
||||
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
|
||||
}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
@@ -309,9 +317,9 @@ public:
|
||||
template<typename... Matcher>
|
||||
void connect(basic_registry<entity_type> ®, basic_collector<Matcher...>) {
|
||||
disconnect();
|
||||
connect<Matcher...>(reg, std::make_index_sequence<sizeof...(Matcher)>{});
|
||||
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
|
||||
target = ®
|
||||
view.reset();
|
||||
view.clear();
|
||||
}
|
||||
|
||||
/*! @brief Disconnects an observer from the registry it keeps track of. */
|
||||
@@ -326,7 +334,7 @@ public:
|
||||
* @brief Returns the number of elements in an observer.
|
||||
* @return Number of elements.
|
||||
*/
|
||||
size_type size() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
|
||||
return view.size();
|
||||
}
|
||||
|
||||
@@ -334,7 +342,7 @@ public:
|
||||
* @brief Checks whether an observer is empty.
|
||||
* @return True if the observer is empty, false otherwise.
|
||||
*/
|
||||
bool empty() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return view.empty();
|
||||
}
|
||||
|
||||
@@ -345,12 +353,12 @@ public:
|
||||
* always a valid range, even if the container is empty.
|
||||
*
|
||||
* @note
|
||||
* There are no guarantees on the order of the entities. Use `begin` and
|
||||
* `end` if you want to iterate the observer in the expected order.
|
||||
* Entities are in the reverse order as returned by the `begin`/`end`
|
||||
* iterators.
|
||||
*
|
||||
* @return A pointer to the array of entities.
|
||||
*/
|
||||
const entity_type * data() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT {
|
||||
return view.data();
|
||||
}
|
||||
|
||||
@@ -362,7 +370,7 @@ public:
|
||||
*
|
||||
* @return An iterator to the first entity of the observer.
|
||||
*/
|
||||
iterator_type begin() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
|
||||
return view.sparse_set<entity_type>::begin();
|
||||
}
|
||||
|
||||
@@ -376,18 +384,17 @@ public:
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* observer.
|
||||
*/
|
||||
iterator_type end() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
|
||||
return view.sparse_set<entity_type>::end();
|
||||
}
|
||||
|
||||
/*! @brief Resets the underlying container. */
|
||||
void clear() {
|
||||
view.reset();
|
||||
/*! @brief Clears the underlying container. */
|
||||
void clear() ENTT_NOEXCEPT {
|
||||
view.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and applies the given function object to them,
|
||||
* then clears the observer.
|
||||
* @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:
|
||||
@@ -401,20 +408,16 @@ public:
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
static_assert(std::is_invocable_v<Func, entity_type>);
|
||||
std::for_each(begin(), end(), std::move(func));
|
||||
for(const auto entity: *this) {
|
||||
func(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and applies the given function object to them,
|
||||
* then clears the observer.
|
||||
*
|
||||
* 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
|
||||
* @sa each
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
@@ -435,4 +438,4 @@ private:
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_ENTITY_OBSERVER_HPP
|
||||
#endif
|
||||
|
||||
53
src/entt/entity/pool.hpp
Normal file
53
src/entt/entity/pool.hpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef ENTT_ENTITY_POOL_HPP
|
||||
#define ENTT_ENTITY_POOL_HPP
|
||||
|
||||
|
||||
#include <type_traits>
|
||||
#include "storage.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Applies component-to-pool conversion and defines the resulting type as
|
||||
* the member typedef type.
|
||||
*
|
||||
* Formally:
|
||||
*
|
||||
* * If the component type is a non-const one, the member typedef type is the
|
||||
* declared storage type.
|
||||
* * If the component type is a const one, the member typedef type is the
|
||||
* declared storage type, except it has a const-qualifier added.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Type Type of objects assigned to the entities.
|
||||
*/
|
||||
template<typename Entity, typename Type, typename = void>
|
||||
struct pool {
|
||||
/*! @brief Resulting type after component-to-pool conversion. */
|
||||
using type = storage<Entity, Type>;
|
||||
};
|
||||
|
||||
|
||||
/*! @copydoc pool */
|
||||
template<typename Entity, typename Type>
|
||||
struct pool<Entity, const Type> {
|
||||
/*! @brief Resulting type after component-to-pool conversion. */
|
||||
using type = std::add_const_t<typename pool<Entity, std::remove_const_t<Type>>::type>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Alias declaration to use to make component-to-pool conversions.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Type Type of objects assigned to the entities.
|
||||
*/
|
||||
template<typename Entity, typename Type>
|
||||
using pool_t = typename pool<Entity, Type>::type;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,14 +3,12 @@
|
||||
|
||||
|
||||
#include <iterator>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "sparse_set.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
@@ -50,7 +48,7 @@ namespace entt {
|
||||
* have a valid reference and won't be updated accordingly).
|
||||
*
|
||||
* @warning
|
||||
* Lifetime of a view must overcome the one of the registry that generated it.
|
||||
* 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).
|
||||
@@ -60,72 +58,80 @@ class basic_runtime_view {
|
||||
/*! @brief A registry is allowed to create views. */
|
||||
friend class basic_registry<Entity>;
|
||||
|
||||
using underlying_iterator_type = typename sparse_set<Entity>::iterator_type;
|
||||
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
|
||||
using underlying_iterator = typename sparse_set<Entity>::iterator;
|
||||
|
||||
class iterator {
|
||||
class view_iterator final {
|
||||
friend class basic_runtime_view<Entity>;
|
||||
|
||||
iterator(underlying_iterator_type first, underlying_iterator_type last, const sparse_set<Entity> * const *others, const sparse_set<Entity> * const *length) ENTT_NOEXCEPT
|
||||
: begin{first},
|
||||
end{last},
|
||||
from{others},
|
||||
to{length}
|
||||
view_iterator(const std::vector<const sparse_set<Entity> *> &cpools, const std::vector<const sparse_set<Entity> *> &ignore, underlying_iterator curr) ENTT_NOEXCEPT
|
||||
: pools{&cpools},
|
||||
filter{&ignore},
|
||||
it{curr}
|
||||
{
|
||||
if(begin != end && !valid()) {
|
||||
if(it != (*pools)[0]->end() && !valid()) {
|
||||
++(*this);
|
||||
}
|
||||
}
|
||||
|
||||
bool valid() const ENTT_NOEXCEPT {
|
||||
return std::all_of(from, to, [entt = *begin](const auto *view) {
|
||||
return view->has(entt);
|
||||
});
|
||||
[[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 difference_type = typename underlying_iterator_type::difference_type;
|
||||
using value_type = typename underlying_iterator_type::value_type;
|
||||
using pointer = typename underlying_iterator_type::pointer;
|
||||
using reference = typename underlying_iterator_type::reference;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = typename underlying_iterator::difference_type;
|
||||
using value_type = typename underlying_iterator::value_type;
|
||||
using pointer = typename underlying_iterator::pointer;
|
||||
using reference = typename underlying_iterator::reference;
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
|
||||
iterator() ENTT_NOEXCEPT = default;
|
||||
view_iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
iterator & operator++() ENTT_NOEXCEPT {
|
||||
return (++begin != end && !valid()) ? ++(*this) : *this;
|
||||
view_iterator & operator++() {
|
||||
while(++it != (*pools)[0]->end() && !valid());
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int) ENTT_NOEXCEPT {
|
||||
iterator orig = *this;
|
||||
view_iterator operator++(int) {
|
||||
view_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
bool operator==(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.begin == begin;
|
||||
view_iterator & operator--() ENTT_NOEXCEPT {
|
||||
while(--it != (*pools)[0]->begin() && !valid());
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
|
||||
view_iterator operator--(int) ENTT_NOEXCEPT {
|
||||
view_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const view_iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.it == it;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator!=(const view_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
pointer operator->() const ENTT_NOEXCEPT {
|
||||
return begin.operator->();
|
||||
[[nodiscard]] pointer operator->() const {
|
||||
return it.operator->();
|
||||
}
|
||||
|
||||
reference operator*() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] reference operator*() const {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
private:
|
||||
underlying_iterator_type begin;
|
||||
underlying_iterator_type end;
|
||||
const sparse_set<Entity> * const *from;
|
||||
const sparse_set<Entity> * const *to;
|
||||
const std::vector<const sparse_set<Entity> *> *pools;
|
||||
const std::vector<const sparse_set<Entity> *> *filter;
|
||||
underlying_iterator it;
|
||||
};
|
||||
|
||||
basic_runtime_view(std::vector<const sparse_set<Entity> *> others) ENTT_NOEXCEPT
|
||||
: pools{std::move(others)}
|
||||
basic_runtime_view(std::vector<const sparse_set<Entity> *> cpools, std::vector<const sparse_set<Entity> *> epools) ENTT_NOEXCEPT
|
||||
: pools{std::move(cpools)},
|
||||
filter{std::move(epools)}
|
||||
{
|
||||
const auto it = std::min_element(pools.begin(), pools.end(), [](const auto *lhs, const auto *rhs) {
|
||||
return (!lhs && rhs) || (lhs && rhs && lhs->size() < rhs->size());
|
||||
@@ -135,23 +141,23 @@ class basic_runtime_view {
|
||||
std::rotate(pools.begin(), it, pools.end());
|
||||
}
|
||||
|
||||
bool valid() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool valid() const {
|
||||
return !pools.empty() && pools.front();
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename sparse_set<Entity>::entity_type;
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename sparse_set<Entity>::size_type;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = iterator;
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Bidirectional iterator type. */
|
||||
using iterator = view_iterator;
|
||||
|
||||
/**
|
||||
* @brief Estimates the number of entities that have the given components.
|
||||
* @return Estimated number of entities that have the given components.
|
||||
*/
|
||||
size_type size() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] size_type size() const {
|
||||
return valid() ? pools.front()->size() : size_type{};
|
||||
}
|
||||
|
||||
@@ -159,7 +165,7 @@ public:
|
||||
* @brief Checks if the view is definitely empty.
|
||||
* @return True if the view is definitely empty, false otherwise.
|
||||
*/
|
||||
bool empty() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool empty() const {
|
||||
return !valid() || pools.front()->empty();
|
||||
}
|
||||
|
||||
@@ -172,21 +178,13 @@ public:
|
||||
* `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* Iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
iterator_type begin() const ENTT_NOEXCEPT {
|
||||
iterator_type it{};
|
||||
|
||||
if(valid()) {
|
||||
const auto &pool = *pools.front();
|
||||
const auto * const *data = pools.data();
|
||||
it = { pool.begin(), pool.end(), data + 1, data + pools.size() };
|
||||
}
|
||||
|
||||
return it;
|
||||
[[nodiscard]] iterator begin() const {
|
||||
return valid() ? iterator{pools, filter, pools[0]->begin()} : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,21 +196,14 @@ public:
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* Iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
*/
|
||||
iterator_type end() const ENTT_NOEXCEPT {
|
||||
iterator_type it{};
|
||||
|
||||
if(valid()) {
|
||||
const auto &pool = *pools.front();
|
||||
it = { pool.end(), pool.end(), nullptr, nullptr };
|
||||
}
|
||||
|
||||
return it;
|
||||
[[nodiscard]] iterator end() const {
|
||||
return valid() ? iterator{pools, filter, pools[0]->end()} : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,10 +211,9 @@ public:
|
||||
* @param entt A valid entity identifier.
|
||||
* @return True if the view contains the given entity, false otherwise.
|
||||
*/
|
||||
bool contains(const entity_type entt) const ENTT_NOEXCEPT {
|
||||
return valid() && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *view) {
|
||||
return view->find(entt) != view->end();
|
||||
});
|
||||
[[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); });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,15 +233,18 @@ public:
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
std::for_each(begin(), end(), func);
|
||||
for(const auto entity: *this) {
|
||||
func(entity);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<const sparse_set<Entity> *> pools;
|
||||
std::vector<const sparse_set<Entity> *> filter;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_ENTITY_RUNTIME_VIEW_HPP
|
||||
#endif
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <iterator>
|
||||
@@ -31,14 +32,7 @@ class basic_snapshot {
|
||||
/*! @brief A registry is allowed to create snapshots. */
|
||||
friend class basic_registry<Entity>;
|
||||
|
||||
using follow_fn_type = Entity(const basic_registry<Entity> &, const Entity);
|
||||
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
|
||||
|
||||
basic_snapshot(const basic_registry<Entity> *source, Entity init, follow_fn_type *fn) ENTT_NOEXCEPT
|
||||
: reg{source},
|
||||
seed{init},
|
||||
follow{fn}
|
||||
{}
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
template<typename Component, typename Archive, typename It>
|
||||
void get(Archive &archive, std::size_t sz, It first, It last) const {
|
||||
@@ -71,6 +65,17 @@ class basic_snapshot {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -78,10 +83,10 @@ public:
|
||||
basic_snapshot & operator=(basic_snapshot &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Puts aside all the entities that are still in use.
|
||||
* @brief Puts aside all the entities from the underlying registry.
|
||||
*
|
||||
* Entities are serialized along with their versions. Destroyed entities are
|
||||
* not taken in consideration by this function.
|
||||
* taken in consideration as well by this function.
|
||||
*
|
||||
* @tparam Archive Type of output archive.
|
||||
* @param archive A valid reference to an output archive.
|
||||
@@ -89,34 +94,14 @@ public:
|
||||
*/
|
||||
template<typename Archive>
|
||||
const basic_snapshot & entities(Archive &archive) const {
|
||||
archive(typename traits_type::entity_type(reg->alive()));
|
||||
reg->each([&archive](const auto entt) { archive(entt); });
|
||||
return *this;
|
||||
}
|
||||
const auto sz = reg->size();
|
||||
auto first = reg->data();
|
||||
const auto last = first + sz;
|
||||
|
||||
/**
|
||||
* @brief Puts aside destroyed entities.
|
||||
*
|
||||
* Entities are serialized along with their versions. Entities that are
|
||||
* still in use are not taken in consideration 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 & destroyed(Archive &archive) const {
|
||||
auto size = reg->size() - reg->alive();
|
||||
archive(typename traits_type::entity_type(size));
|
||||
archive(typename traits_type::entity_type(sz));
|
||||
|
||||
if(size) {
|
||||
auto curr = seed;
|
||||
archive(curr);
|
||||
|
||||
for(--size; size; --size) {
|
||||
curr = follow(*reg, curr);
|
||||
archive(curr);
|
||||
}
|
||||
while(first != last) {
|
||||
archive(*(first++));
|
||||
}
|
||||
|
||||
return *this;
|
||||
@@ -135,25 +120,7 @@ public:
|
||||
*/
|
||||
template<typename... Component, typename Archive>
|
||||
const basic_snapshot & component(Archive &archive) const {
|
||||
if constexpr(sizeof...(Component) == 1) {
|
||||
const auto sz = reg->template size<Component...>();
|
||||
const auto *entities = reg->template data<Component...>();
|
||||
|
||||
archive(typename traits_type::entity_type(sz));
|
||||
|
||||
for(std::remove_const_t<decltype(sz)> pos{}; pos < sz; ++pos) {
|
||||
const auto entt = entities[pos];
|
||||
|
||||
if constexpr(std::is_empty_v<Component...>) {
|
||||
archive(entt);
|
||||
} else {
|
||||
archive(entt, reg->template get<Component...>(entt));
|
||||
}
|
||||
};
|
||||
} else {
|
||||
(component<Component>(archive), ...);
|
||||
}
|
||||
|
||||
(component<Component>(archive, reg->template data<Component>(), reg->template data<Component>() + reg->template size<Component>()), ...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -173,14 +140,12 @@ public:
|
||||
*/
|
||||
template<typename... Component, typename Archive, typename It>
|
||||
const basic_snapshot & component(Archive &archive, It first, It last) const {
|
||||
component<Component...>(archive, first, last, std::make_index_sequence<sizeof...(Component)>{});
|
||||
component<Component...>(archive, first, last, std::index_sequence_for<Component...>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
const basic_registry<Entity> *reg;
|
||||
const Entity seed;
|
||||
follow_fn_type *follow;
|
||||
const basic_registry<entity_type> *reg;
|
||||
};
|
||||
|
||||
|
||||
@@ -199,52 +164,49 @@ class basic_snapshot_loader {
|
||||
/*! @brief A registry is allowed to create snapshot loaders. */
|
||||
friend class basic_registry<Entity>;
|
||||
|
||||
using force_fn_type = void(basic_registry<Entity> &, const Entity, const bool);
|
||||
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
basic_snapshot_loader(basic_registry<Entity> *source, force_fn_type *fn) ENTT_NOEXCEPT
|
||||
: reg{source},
|
||||
force{fn}
|
||||
{
|
||||
// to restore a snapshot as a whole requires a clean registry
|
||||
ENTT_ASSERT(reg->empty());
|
||||
}
|
||||
|
||||
template<typename Archive>
|
||||
void assure(Archive &archive, bool discard) const {
|
||||
template<typename Type, typename Archive>
|
||||
void assign(Archive &archive) const {
|
||||
typename traits_type::entity_type length{};
|
||||
archive(length);
|
||||
|
||||
while(length--) {
|
||||
Entity entt{};
|
||||
archive(entt);
|
||||
force(*reg, entt, discard);
|
||||
}
|
||||
}
|
||||
entity_type entt{};
|
||||
|
||||
template<typename Type, typename Archive, typename... Args>
|
||||
void assign(Archive &archive, Args... args) const {
|
||||
typename traits_type::entity_type length{};
|
||||
archive(length);
|
||||
|
||||
while(length--) {
|
||||
static constexpr auto discard = false;
|
||||
Entity entt{};
|
||||
|
||||
if constexpr(std::is_empty_v<Type>) {
|
||||
if constexpr(std::is_empty_v<Type>) {
|
||||
while(length--) {
|
||||
archive(entt);
|
||||
force(*reg, entt, discard);
|
||||
reg->template assign<Type>(args..., entt);
|
||||
} else {
|
||||
Type instance{};
|
||||
const auto entity = reg->valid(entt) ? entt : reg->create(entt);
|
||||
ENTT_ASSERT(entity == entt);
|
||||
reg->template emplace<Type>(entity);
|
||||
}
|
||||
} else {
|
||||
Type instance{};
|
||||
|
||||
while(length--) {
|
||||
archive(entt, instance);
|
||||
force(*reg, entt, discard);
|
||||
reg->template assign<Type>(args..., entt, std::as_const(instance));
|
||||
const auto entity = reg->valid(entt) ? entt : reg->create(entt);
|
||||
ENTT_ASSERT(entity == entt);
|
||||
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());
|
||||
}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
basic_snapshot_loader(basic_snapshot_loader &&) = default;
|
||||
|
||||
@@ -263,25 +225,17 @@ public:
|
||||
*/
|
||||
template<typename Archive>
|
||||
const basic_snapshot_loader & entities(Archive &archive) const {
|
||||
static constexpr auto discard = false;
|
||||
assure(archive, discard);
|
||||
return *this;
|
||||
}
|
||||
typename traits_type::entity_type length{};
|
||||
|
||||
archive(length);
|
||||
std::vector<entity_type> all(length);
|
||||
|
||||
for(decltype(length) pos{}; pos < length; ++pos) {
|
||||
archive(all[pos]);
|
||||
}
|
||||
|
||||
reg->assign(all.cbegin(), all.cend());
|
||||
|
||||
/**
|
||||
* @brief Restores entities that were destroyed during serialization.
|
||||
*
|
||||
* This function restores the entities that were destroyed 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 & destroyed(Archive &archive) const {
|
||||
static constexpr auto discard = true;
|
||||
assure(archive, discard);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -323,8 +277,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
basic_registry<Entity> *reg;
|
||||
force_fn_type *force;
|
||||
basic_registry<entity_type> *reg;
|
||||
};
|
||||
|
||||
|
||||
@@ -332,7 +285,7 @@ private:
|
||||
* @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 accomodate in a registry
|
||||
* (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.
|
||||
@@ -346,7 +299,7 @@ private:
|
||||
*/
|
||||
template<typename Entity>
|
||||
class basic_continuous_loader {
|
||||
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
void destroy(Entity entt) {
|
||||
const auto it = remloc.find(entt);
|
||||
@@ -371,39 +324,59 @@ class basic_continuous_loader {
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Other, typename Type, typename Member>
|
||||
void update(Other &instance, Member Type:: *member) {
|
||||
if constexpr(!std::is_same_v<Other, Type>) {
|
||||
return;
|
||||
} else if constexpr(std::is_same_v<Member, Entity>) {
|
||||
instance.*member = map(instance.*member);
|
||||
} else {
|
||||
// maybe a container? let's try...
|
||||
for(auto &entt: instance.*member) {
|
||||
entt = map(entt);
|
||||
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 Archive>
|
||||
void assure(Archive &archive, void(basic_continuous_loader:: *member)(Entity)) {
|
||||
typename traits_type::entity_type length{};
|
||||
archive(length);
|
||||
|
||||
while(length--) {
|
||||
Entity entt{};
|
||||
archive(entt);
|
||||
(this->*member)(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 reset() {
|
||||
void remove_if_exists() {
|
||||
for(auto &&ref: remloc) {
|
||||
const auto local = ref.second.first;
|
||||
|
||||
if(reg->valid(local)) {
|
||||
reg->template reset<Component>(local);
|
||||
reg->template remove_if_exists<Component>(local);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -413,19 +386,22 @@ class basic_continuous_loader {
|
||||
typename traits_type::entity_type length{};
|
||||
archive(length);
|
||||
|
||||
while(length--) {
|
||||
Entity entt{};
|
||||
entity_type entt{};
|
||||
|
||||
if constexpr(std::is_empty_v<Other>) {
|
||||
if constexpr(std::is_empty_v<Other>) {
|
||||
while(length--) {
|
||||
archive(entt);
|
||||
restore(entt);
|
||||
reg->template assign_or_replace<Other>(map(entt));
|
||||
} else {
|
||||
Other instance{};
|
||||
reg->template emplace_or_replace<Other>(map(entt));
|
||||
}
|
||||
} else {
|
||||
Other instance{};
|
||||
|
||||
while(length--) {
|
||||
archive(entt, instance);
|
||||
(update(instance, member), ...);
|
||||
restore(entt);
|
||||
reg->template assign_or_replace<Other>(map(entt), std::as_const(instance));
|
||||
reg->template emplace_or_replace<Other>(map(entt), std::move(instance));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -435,7 +411,7 @@ public:
|
||||
using entity_type = Entity;
|
||||
|
||||
/**
|
||||
* @brief Constructs a loader that is bound to a given registry.
|
||||
* @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
|
||||
@@ -460,23 +436,21 @@ public:
|
||||
*/
|
||||
template<typename Archive>
|
||||
basic_continuous_loader & entities(Archive &archive) {
|
||||
assure(archive, &basic_continuous_loader::restore);
|
||||
return *this;
|
||||
}
|
||||
typename traits_type::entity_type length{};
|
||||
entity_type entt{};
|
||||
|
||||
archive(length);
|
||||
|
||||
for(decltype(length) pos{}; pos < length; ++pos) {
|
||||
archive(entt);
|
||||
|
||||
if(const auto entity = (to_integral(entt) & traits_type::entity_mask); entity == pos) {
|
||||
restore(entt);
|
||||
} else {
|
||||
destroy(entt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restores entities that were destroyed during serialization.
|
||||
*
|
||||
* This function restores the entities that were destroyed 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 & destroyed(Archive &archive) {
|
||||
assure(archive, &basic_continuous_loader::destroy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -501,7 +475,7 @@ public:
|
||||
*/
|
||||
template<typename... Component, typename Archive, typename... Type, typename... Member>
|
||||
basic_continuous_loader & component(Archive &archive, Member Type:: *... member) {
|
||||
(reset<Component>(), ...);
|
||||
(remove_if_exists<Component>(), ...);
|
||||
(assign<Component>(archive, member...), ...);
|
||||
return *this;
|
||||
}
|
||||
@@ -559,7 +533,7 @@ public:
|
||||
* @param entt An entity identifier.
|
||||
* @return True if `entity` is managed by the loader, false otherwise.
|
||||
*/
|
||||
bool has(entity_type entt) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool contains(entity_type entt) const ENTT_NOEXCEPT {
|
||||
return (remloc.find(entt) != remloc.cend());
|
||||
}
|
||||
|
||||
@@ -568,7 +542,7 @@ public:
|
||||
* @param entt An entity identifier.
|
||||
* @return The local identifier if any, the null entity otherwise.
|
||||
*/
|
||||
entity_type map(entity_type entt) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] entity_type map(entity_type entt) const ENTT_NOEXCEPT {
|
||||
const auto it = remloc.find(entt);
|
||||
entity_type other = null;
|
||||
|
||||
@@ -588,4 +562,4 @@ private:
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_ENTITY_SNAPSHOT_HPP
|
||||
#endif
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
#define ENTT_ENTITY_SPARSE_SET_HPP
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstddef>
|
||||
#include <numeric>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "../core/algorithm.hpp"
|
||||
@@ -35,32 +33,28 @@ namespace entt {
|
||||
* purpose of the framework.
|
||||
*
|
||||
* @note
|
||||
* 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.
|
||||
*
|
||||
* @note
|
||||
* Internal data structures arrange elements to maximize performance. Because of
|
||||
* that, there are no guarantees that elements have the expected order when
|
||||
* iterate directly the internal packed array (see `data` and `size` member
|
||||
* functions for that). Use `begin` and `end` instead.
|
||||
* 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).
|
||||
*/
|
||||
template<typename Entity>
|
||||
class sparse_set {
|
||||
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
|
||||
static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two");
|
||||
static constexpr auto entt_per_page = ENTT_PAGE_SIZE / sizeof(Entity);
|
||||
|
||||
static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0));
|
||||
static constexpr auto entt_per_page = ENTT_PAGE_SIZE / sizeof(typename traits_type::entity_type);
|
||||
using traits_type = entt_traits<Entity>;
|
||||
using page_type = std::unique_ptr<Entity[]>;
|
||||
|
||||
class iterator {
|
||||
class sparse_set_iterator final {
|
||||
friend class sparse_set<Entity>;
|
||||
|
||||
using direct_type = const std::vector<Entity>;
|
||||
using packed_type = std::vector<Entity>;
|
||||
using index_type = typename traits_type::difference_type;
|
||||
|
||||
iterator(direct_type *ref, const index_type idx) ENTT_NOEXCEPT
|
||||
: direct{ref}, index{idx}
|
||||
sparse_set_iterator(const packed_type &ref, const index_type idx) ENTT_NOEXCEPT
|
||||
: packed{&ref}, index{idx}
|
||||
{}
|
||||
|
||||
public:
|
||||
@@ -70,107 +64,113 @@ class sparse_set {
|
||||
using reference = const value_type &;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
iterator() ENTT_NOEXCEPT = default;
|
||||
sparse_set_iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
iterator & operator++() ENTT_NOEXCEPT {
|
||||
sparse_set_iterator & operator++() ENTT_NOEXCEPT {
|
||||
return --index, *this;
|
||||
}
|
||||
|
||||
iterator operator++(int) ENTT_NOEXCEPT {
|
||||
sparse_set_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
iterator & operator--() ENTT_NOEXCEPT {
|
||||
sparse_set_iterator & operator--() ENTT_NOEXCEPT {
|
||||
return ++index, *this;
|
||||
}
|
||||
|
||||
iterator operator--(int) ENTT_NOEXCEPT {
|
||||
iterator orig = *this;
|
||||
return --(*this), orig;
|
||||
sparse_set_iterator operator--(int) ENTT_NOEXCEPT {
|
||||
sparse_set_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
|
||||
sparse_set_iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
|
||||
index -= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return iterator{direct, index-value};
|
||||
sparse_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
||||
sparse_set_iterator copy = *this;
|
||||
return (copy += value);
|
||||
}
|
||||
|
||||
iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
|
||||
sparse_set_iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
|
||||
return (*this += -value);
|
||||
}
|
||||
|
||||
iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
||||
sparse_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return (*this + -value);
|
||||
}
|
||||
|
||||
difference_type operator-(const iterator &other) const ENTT_NOEXCEPT {
|
||||
difference_type operator-(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.index - index;
|
||||
}
|
||||
|
||||
reference operator[](const difference_type value) const ENTT_NOEXCEPT {
|
||||
const auto pos = size_type(index-value-1);
|
||||
return (*direct)[pos];
|
||||
[[nodiscard]] reference operator[](const difference_type value) const {
|
||||
const auto pos = size_type(index-value-1u);
|
||||
return (*packed)[pos];
|
||||
}
|
||||
|
||||
bool operator==(const iterator &other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool operator==(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.index == index;
|
||||
}
|
||||
|
||||
bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool operator!=(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const iterator &other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool operator<(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
|
||||
return index > other.index;
|
||||
}
|
||||
|
||||
bool operator>(const iterator &other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool operator>(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
|
||||
return index < other.index;
|
||||
}
|
||||
|
||||
bool operator<=(const iterator &other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool operator<=(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this > other);
|
||||
}
|
||||
|
||||
bool operator>=(const iterator &other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool operator>=(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this < other);
|
||||
}
|
||||
|
||||
pointer operator->() const ENTT_NOEXCEPT {
|
||||
const auto pos = size_type(index-1);
|
||||
return &(*direct)[pos];
|
||||
[[nodiscard]] pointer operator->() const {
|
||||
const auto pos = size_type(index-1u);
|
||||
return &(*packed)[pos];
|
||||
}
|
||||
|
||||
reference operator*() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] reference operator*() const {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
private:
|
||||
direct_type *direct;
|
||||
const packed_type *packed;
|
||||
index_type index;
|
||||
};
|
||||
|
||||
void assure(const std::size_t page) {
|
||||
if(!(page < reverse.size())) {
|
||||
reverse.resize(page+1);
|
||||
}
|
||||
|
||||
if(!reverse[page]) {
|
||||
reverse[page] = std::make_unique<entity_type[]>(entt_per_page);
|
||||
// null is safe in all cases for our purposes
|
||||
std::fill_n(reverse[page].get(), entt_per_page, null);
|
||||
}
|
||||
[[nodiscard]] auto page(const Entity entt) const ENTT_NOEXCEPT {
|
||||
return size_type{(to_integral(entt) & traits_type::entity_mask) / entt_per_page};
|
||||
}
|
||||
|
||||
auto map(const Entity entt) const ENTT_NOEXCEPT {
|
||||
const auto identifier = to_integer(entt) & traits_type::entity_mask;
|
||||
const auto page = size_type(identifier / entt_per_page);
|
||||
const auto offset = size_type(identifier & (entt_per_page - 1));
|
||||
return std::make_pair(page, offset);
|
||||
[[nodiscard]] auto offset(const Entity entt) const ENTT_NOEXCEPT {
|
||||
return size_type{to_integral(entt) & (entt_per_page - 1)};
|
||||
}
|
||||
|
||||
[[nodiscard]] page_type & assure(const std::size_t pos) {
|
||||
if(!(pos < sparse.size())) {
|
||||
sparse.resize(pos+1);
|
||||
}
|
||||
|
||||
if(!sparse[pos]) {
|
||||
sparse[pos].reset(new entity_type[entt_per_page]);
|
||||
// null is safe in all cases for our purposes
|
||||
for(auto *first = sparse[pos].get(), *last = first + entt_per_page; first != last; ++first) {
|
||||
*first = null;
|
||||
}
|
||||
}
|
||||
|
||||
return sparse[pos];
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -179,46 +179,18 @@ public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator_type = iterator;
|
||||
using iterator = sparse_set_iterator;
|
||||
/*! @brief Reverse iterator type. */
|
||||
using reverse_iterator = const entity_type *;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
sparse_set() = default;
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
*/
|
||||
sparse_set(const sparse_set &other)
|
||||
: reverse{},
|
||||
direct{other.direct}
|
||||
{
|
||||
for(size_type pos{}, last = other.reverse.size(); pos < last; ++pos) {
|
||||
if(other.reverse[pos]) {
|
||||
assure(pos);
|
||||
std::copy_n(other.reverse[pos].get(), entt_per_page, reverse[pos].get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
sparse_set(sparse_set &&) = default;
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
virtual ~sparse_set() ENTT_NOEXCEPT = default;
|
||||
|
||||
/**
|
||||
* @brief Copy assignment operator.
|
||||
* @param other The instance to copy from.
|
||||
* @return This sparse set.
|
||||
*/
|
||||
sparse_set & operator=(const sparse_set &other) {
|
||||
if(&other != this) {
|
||||
auto tmp{other};
|
||||
*this = std::move(tmp);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
virtual ~sparse_set() = default;
|
||||
|
||||
/*! @brief Default move assignment operator. @return This sparse set. */
|
||||
sparse_set & operator=(sparse_set &&) = default;
|
||||
@@ -232,7 +204,7 @@ public:
|
||||
* @param cap Desired capacity.
|
||||
*/
|
||||
void reserve(const size_type cap) {
|
||||
direct.reserve(cap);
|
||||
packed.reserve(cap);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,19 +212,19 @@ public:
|
||||
* allocated space for.
|
||||
* @return Capacity of the sparse set.
|
||||
*/
|
||||
size_type capacity() const ENTT_NOEXCEPT {
|
||||
return direct.capacity();
|
||||
[[nodiscard]] size_type capacity() const ENTT_NOEXCEPT {
|
||||
return packed.capacity();
|
||||
}
|
||||
|
||||
/*! @brief Requests the removal of unused capacity. */
|
||||
void shrink_to_fit() {
|
||||
// conservative approach
|
||||
if(direct.empty()) {
|
||||
reverse.clear();
|
||||
if(packed.empty()) {
|
||||
sparse.clear();
|
||||
}
|
||||
|
||||
reverse.shrink_to_fit();
|
||||
direct.shrink_to_fit();
|
||||
sparse.shrink_to_fit();
|
||||
packed.shrink_to_fit();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,8 +237,8 @@ public:
|
||||
*
|
||||
* @return Extent of the sparse set.
|
||||
*/
|
||||
size_type extent() const ENTT_NOEXCEPT {
|
||||
return reverse.size() * entt_per_page;
|
||||
[[nodiscard]] size_type extent() const ENTT_NOEXCEPT {
|
||||
return sparse.size() * entt_per_page;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,16 +251,16 @@ public:
|
||||
*
|
||||
* @return Number of elements.
|
||||
*/
|
||||
size_type size() const ENTT_NOEXCEPT {
|
||||
return direct.size();
|
||||
[[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.
|
||||
*/
|
||||
bool empty() const ENTT_NOEXCEPT {
|
||||
return direct.empty();
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return packed.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,16 +270,13 @@ public:
|
||||
* always a valid range, even if the container is empty.
|
||||
*
|
||||
* @note
|
||||
* There are no guarantees on the order, even though `respect` has been
|
||||
* previously invoked. Internal data structures arrange elements to maximize
|
||||
* performance. Accessing them directly gives a performance boost but less
|
||||
* guarantees. Use `begin` and `end` if you want to iterate the sparse set
|
||||
* in the expected order.
|
||||
* Entities are in the reverse order as returned by the `begin`/`end`
|
||||
* iterators.
|
||||
*
|
||||
* @return A pointer to the internal packed array.
|
||||
*/
|
||||
const entity_type * data() const ENTT_NOEXCEPT {
|
||||
return direct.data();
|
||||
[[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT {
|
||||
return packed.data();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -317,15 +286,11 @@ public:
|
||||
* array. If the sparse set is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @note
|
||||
* Random access iterators stay true to the order imposed by a call to
|
||||
* `respect`.
|
||||
*
|
||||
* @return An iterator to the first entity of the internal packed array.
|
||||
*/
|
||||
iterator_type begin() const ENTT_NOEXCEPT {
|
||||
const typename traits_type::difference_type pos = direct.size();
|
||||
return iterator_type{&direct, pos};
|
||||
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
|
||||
const typename traits_type::difference_type pos = packed.size();
|
||||
return iterator{packed, pos};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -335,15 +300,39 @@ public:
|
||||
* the internal packed array. Attempting to dereference the returned
|
||||
* iterator results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Random access iterators stay true to the order imposed by a call to
|
||||
* `respect`.
|
||||
*
|
||||
* @return An iterator to the element following the last entity of the
|
||||
* internal packed array.
|
||||
*/
|
||||
iterator_type end() const ENTT_NOEXCEPT {
|
||||
return iterator_type{&direct, {}};
|
||||
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
|
||||
return iterator{packed, {}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @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]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
|
||||
return packed.data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reverse iterator to the end.
|
||||
*
|
||||
* The returned iterator points to the element following the last entity in
|
||||
* the reversed internal packed array. Attempting to dereference the
|
||||
* returned iterator results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the element following the last entity of the
|
||||
* reversed internal packed array.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
|
||||
return rbegin() + packed.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -352,8 +341,8 @@ public:
|
||||
* @return An iterator to the given entity if it's found, past the end
|
||||
* iterator otherwise.
|
||||
*/
|
||||
iterator_type find(const entity_type entt) const ENTT_NOEXCEPT {
|
||||
return has(entt) ? --(end() - index(entt)) : end();
|
||||
[[nodiscard]] iterator find(const entity_type entt) const {
|
||||
return contains(entt) ? --(end() - index(entt)) : end();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -361,10 +350,10 @@ public:
|
||||
* @param entt A valid entity identifier.
|
||||
* @return True if the sparse set contains the entity, false otherwise.
|
||||
*/
|
||||
bool has(const entity_type entt) const ENTT_NOEXCEPT {
|
||||
auto [page, offset] = map(entt);
|
||||
// testing against null permits to avoid accessing the direct vector
|
||||
return (page < reverse.size() && reverse[page] && reverse[page][offset] != null);
|
||||
[[nodiscard]] bool contains(const entity_type entt) const {
|
||||
const auto curr = page(entt);
|
||||
// testing against null permits to avoid accessing the packed array
|
||||
return (curr < sparse.size() && sparse[curr] && sparse[curr][offset(entt)] != null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -379,10 +368,9 @@ public:
|
||||
* @param entt A valid entity identifier.
|
||||
* @return The position of the entity in the sparse set.
|
||||
*/
|
||||
size_type index(const entity_type entt) const ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(has(entt));
|
||||
auto [page, offset] = map(entt);
|
||||
return size_type(reverse[page][offset]);
|
||||
[[nodiscard]] size_type index(const entity_type entt) const {
|
||||
ENTT_ASSERT(contains(entt));
|
||||
return size_type{to_integral(sparse[page(entt)][offset(entt)])};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -396,12 +384,10 @@ public:
|
||||
*
|
||||
* @param entt A valid entity identifier.
|
||||
*/
|
||||
void construct(const entity_type entt) {
|
||||
ENTT_ASSERT(!has(entt));
|
||||
auto [page, offset] = map(entt);
|
||||
assure(page);
|
||||
reverse[page][offset] = entity_type(direct.size());
|
||||
direct.push_back(entt);
|
||||
void emplace(const entity_type entt) {
|
||||
ENTT_ASSERT(!contains(entt));
|
||||
assure(page(entt))[offset(entt)] = entity_type(static_cast<typename traits_type::entity_type>(packed.size()));
|
||||
packed.push_back(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -413,20 +399,20 @@ public:
|
||||
* An assertion will abort the execution at runtime in debug mode if the
|
||||
* sparse set already contains the given entity.
|
||||
*
|
||||
* @tparam It Type of forward iterator.
|
||||
* @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 batch(It first, It last) {
|
||||
std::for_each(std::make_reverse_iterator(last), std::make_reverse_iterator(first), [this, next = direct.size()](const auto entt) mutable {
|
||||
ENTT_ASSERT(!has(entt));
|
||||
auto [page, offset] = map(entt);
|
||||
assure(page);
|
||||
reverse[page][offset] = entity_type(next++);
|
||||
});
|
||||
void insert(It first, It last) {
|
||||
auto next = static_cast<typename traits_type::entity_type>(packed.size());
|
||||
packed.insert(packed.end(), first, last);
|
||||
|
||||
direct.insert(direct.end(), first, last);
|
||||
while(first != last) {
|
||||
const auto entt = *(first++);
|
||||
ENTT_ASSERT(!contains(entt));
|
||||
assure(page(entt))[offset(entt)] = entity_type(next++);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -440,14 +426,14 @@ public:
|
||||
*
|
||||
* @param entt A valid entity identifier.
|
||||
*/
|
||||
void destroy(const entity_type entt) {
|
||||
ENTT_ASSERT(has(entt));
|
||||
auto [from_page, from_offset] = map(entt);
|
||||
auto [to_page, to_offset] = map(direct.back());
|
||||
direct[size_type(reverse[from_page][from_offset])] = entity_type(direct.back());
|
||||
reverse[to_page][to_offset] = reverse[from_page][from_offset];
|
||||
reverse[from_page][from_offset] = null;
|
||||
direct.pop_back();
|
||||
void erase(const entity_type entt) {
|
||||
ENTT_ASSERT(contains(entt));
|
||||
const auto curr = page(entt);
|
||||
const auto pos = offset(entt);
|
||||
packed[size_type{to_integral(sparse[curr][pos])}] = packed.back();
|
||||
sparse[page(packed.back())][offset(packed.back())] = sparse[curr][pos];
|
||||
sparse[curr][pos] = null;
|
||||
packed.pop_back();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -462,16 +448,14 @@ public:
|
||||
* An assertion will abort the execution at runtime in debug mode if the
|
||||
* sparse set doesn't contain the given entities.
|
||||
*
|
||||
* @param lhs A valid position within the sparse set.
|
||||
* @param rhs A valid position within the sparse set.
|
||||
* @param lhs A valid entity identifier.
|
||||
* @param rhs A valid entity identifier.
|
||||
*/
|
||||
virtual void swap(const size_type lhs, const size_type rhs) ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(lhs < direct.size());
|
||||
ENTT_ASSERT(rhs < direct.size());
|
||||
auto [src_page, src_offset] = map(direct[lhs]);
|
||||
auto [dst_page, dst_offset] = map(direct[rhs]);
|
||||
std::swap(reverse[src_page][src_offset], reverse[dst_page][dst_offset]);
|
||||
std::swap(direct[lhs], direct[rhs]);
|
||||
virtual void swap(const entity_type lhs, const entity_type rhs) {
|
||||
auto &from = sparse[page(lhs)][offset(lhs)];
|
||||
auto &to = sparse[page(rhs)][offset(rhs)];
|
||||
std::swap(packed[size_type{to_integral(from)}], packed[size_type{to_integral(to)}]);
|
||||
std::swap(from, to);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -499,15 +483,6 @@ public:
|
||||
* * An iterator past the last element of the range to sort.
|
||||
* * A comparison function to use to compare the elements.
|
||||
*
|
||||
* The comparison function object received by the sort function object
|
||||
* hasn't necessarily the type of the one passed along with the other
|
||||
* parameters to this member function.
|
||||
*
|
||||
* @note
|
||||
* Attempting to iterate elements using a raw pointer returned by a call to
|
||||
* either `data` or `raw` gives no guarantees on the order, even though
|
||||
* `sort` has been invoked.
|
||||
*
|
||||
* @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.
|
||||
@@ -518,26 +493,71 @@ public:
|
||||
* @param args Arguments to forward to the sort function object, if any.
|
||||
*/
|
||||
template<typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(iterator_type first, iterator_type last, Compare compare, Sort algo = Sort{}, Args &&... args) {
|
||||
ENTT_ASSERT(!(first > last));
|
||||
void sort(iterator first, iterator last, Compare compare, Sort algo = Sort{}, Args &&... args) {
|
||||
ENTT_ASSERT(!(last < first));
|
||||
ENTT_ASSERT(!(last > end()));
|
||||
|
||||
std::vector<size_type> copy(last - first);
|
||||
const auto offset = std::distance(last, end());
|
||||
std::iota(copy.begin(), copy.end(), size_type{});
|
||||
const auto length = std::distance(first, last);
|
||||
const auto skip = std::distance(last, end());
|
||||
const auto to = packed.rend() - skip;
|
||||
const auto from = to - length;
|
||||
|
||||
algo(copy.rbegin(), copy.rend(), [this, offset, compare = std::move(compare)](const auto lhs, const auto rhs) {
|
||||
return compare(std::as_const(direct[lhs+offset]), std::as_const(direct[rhs+offset]));
|
||||
}, std::forward<Args>(args)...);
|
||||
algo(from, to, std::move(compare), std::forward<Args>(args)...);
|
||||
|
||||
for(size_type pos{}, length = copy.size(); pos < length; ++pos) {
|
||||
for(size_type pos = skip, end = skip+length; pos < end; ++pos) {
|
||||
sparse[page(packed[pos])][offset(packed[pos])] = entity_type(static_cast<typename traits_type::entity_type>(pos));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort elements according to the given comparison function.
|
||||
*
|
||||
* @sa sort
|
||||
*
|
||||
* This function is a slightly slower version of `sort` that invokes the
|
||||
* caller to indicate which entities are swapped.<br/>
|
||||
* It's recommended when the caller wants to sort its own data structures to
|
||||
* align them with the order induced in the sparse set.
|
||||
*
|
||||
* The signature of the callback should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* bool(const Entity, const Entity);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Apply Type of function object to invoke to notify the caller.
|
||||
* @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 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 apply A valid function object to use as a callback.
|
||||
* @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 Apply, typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void arrange(iterator first, iterator last, Apply apply, Compare compare, Sort algo = Sort{}, Args &&... args) {
|
||||
ENTT_ASSERT(!(last < first));
|
||||
ENTT_ASSERT(!(last > end()));
|
||||
|
||||
const auto length = std::distance(first, last);
|
||||
const auto skip = std::distance(last, end());
|
||||
const auto to = packed.rend() - skip;
|
||||
const auto from = to - length;
|
||||
|
||||
algo(from, to, std::move(compare), std::forward<Args>(args)...);
|
||||
|
||||
for(size_type pos = skip, end = skip+length; pos < end; ++pos) {
|
||||
auto curr = pos;
|
||||
auto next = copy[curr];
|
||||
auto next = index(packed[curr]);
|
||||
|
||||
while(curr != next) {
|
||||
swap(copy[curr] + offset, copy[next] + offset);
|
||||
copy[curr] = curr;
|
||||
apply(packed[curr], packed[next]);
|
||||
sparse[page(packed[curr])][offset(packed[curr])] = entity_type(static_cast<typename traits_type::entity_type>(curr));
|
||||
|
||||
curr = next;
|
||||
next = copy[curr];
|
||||
next = index(packed[curr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -555,22 +575,18 @@ public:
|
||||
* the expected order after a call to `respect`. See `begin` and `end` for
|
||||
* more details.
|
||||
*
|
||||
* @note
|
||||
* Attempting to iterate elements using the raw pointer returned by `data`
|
||||
* gives no guarantees on the order, even though `respect` has been invoked.
|
||||
*
|
||||
* @param other The sparse sets that imposes the order of the entities.
|
||||
*/
|
||||
void respect(const sparse_set &other) ENTT_NOEXCEPT {
|
||||
void respect(const sparse_set &other) {
|
||||
const auto to = other.end();
|
||||
auto from = other.begin();
|
||||
|
||||
size_type pos = direct.size() - 1;
|
||||
size_type pos = packed.size() - 1;
|
||||
|
||||
while(pos && from != to) {
|
||||
if(has(*from)) {
|
||||
if(*from != direct[pos]) {
|
||||
swap(pos, index(*from));
|
||||
if(contains(*from)) {
|
||||
if(*from != packed[pos]) {
|
||||
swap(packed[pos], *from);
|
||||
}
|
||||
|
||||
--pos;
|
||||
@@ -581,20 +597,20 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resets a sparse set.
|
||||
* @brief Clears a sparse set.
|
||||
*/
|
||||
void reset() {
|
||||
reverse.clear();
|
||||
direct.clear();
|
||||
void clear() ENTT_NOEXCEPT {
|
||||
sparse.clear();
|
||||
packed.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<entity_type[]>> reverse;
|
||||
std::vector<entity_type> direct;
|
||||
std::vector<page_type> sparse;
|
||||
std::vector<entity_type> packed;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_ENTITY_SPARSE_SET_HPP
|
||||
#endif
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "../core/algorithm.hpp"
|
||||
#include "sparse_set.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "sparse_set.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
@@ -32,15 +32,13 @@ namespace entt {
|
||||
* iterators.
|
||||
*
|
||||
* @note
|
||||
* Internal data structures arrange elements to maximize performance. Because of
|
||||
* that, there are no guarantees that elements have the expected order when
|
||||
* iterate directly the internal packed array (see `raw` and `size` member
|
||||
* functions for that). Use `begin` and `end` instead.
|
||||
* Internal data structures arrange elements to maximize performance. There are
|
||||
* no guarantees that objects are returned in the insertion order when iterate
|
||||
* a storage. Do not make assumption on the order in any case.
|
||||
*
|
||||
* @warning
|
||||
* Empty types aren't explicitly instantiated. Temporary objects are returned in
|
||||
* place of the instances of the components and raw access isn't available for
|
||||
* them.
|
||||
* Empty types aren't explicitly instantiated. Therefore, many of the functions
|
||||
* normally available for non-empty types will not be available for empty ones.
|
||||
*
|
||||
* @sa sparse_set<Entity>
|
||||
*
|
||||
@@ -48,19 +46,21 @@ namespace entt {
|
||||
* @tparam Type Type of objects assigned to the entities.
|
||||
*/
|
||||
template<typename Entity, typename Type, typename = std::void_t<>>
|
||||
class basic_storage: public sparse_set<Entity> {
|
||||
class storage: public sparse_set<Entity> {
|
||||
static_assert(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>, "The managed type must be at least move constructible and assignable");
|
||||
|
||||
using underlying_type = sparse_set<Entity>;
|
||||
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
template<bool Const>
|
||||
class iterator {
|
||||
friend class basic_storage<Entity, Type>;
|
||||
class storage_iterator final {
|
||||
friend class storage<Entity, Type>;
|
||||
|
||||
using instance_type = std::conditional_t<Const, const std::vector<Type>, std::vector<Type>>;
|
||||
using index_type = typename traits_type::difference_type;
|
||||
|
||||
iterator(instance_type *ref, const index_type idx) ENTT_NOEXCEPT
|
||||
: instances{ref}, index{idx}
|
||||
storage_iterator(instance_type &ref, const index_type idx) ENTT_NOEXCEPT
|
||||
: instances{&ref}, index{idx}
|
||||
{}
|
||||
|
||||
public:
|
||||
@@ -70,82 +70,83 @@ class basic_storage: public sparse_set<Entity> {
|
||||
using reference = std::conditional_t<Const, const value_type &, value_type &>;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
iterator() ENTT_NOEXCEPT = default;
|
||||
storage_iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
iterator & operator++() ENTT_NOEXCEPT {
|
||||
storage_iterator & operator++() ENTT_NOEXCEPT {
|
||||
return --index, *this;
|
||||
}
|
||||
|
||||
iterator operator++(int) ENTT_NOEXCEPT {
|
||||
iterator orig = *this;
|
||||
storage_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
storage_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
iterator & operator--() ENTT_NOEXCEPT {
|
||||
storage_iterator & operator--() ENTT_NOEXCEPT {
|
||||
return ++index, *this;
|
||||
}
|
||||
|
||||
iterator operator--(int) ENTT_NOEXCEPT {
|
||||
iterator orig = *this;
|
||||
return --(*this), orig;
|
||||
storage_iterator operator--(int) ENTT_NOEXCEPT {
|
||||
storage_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
|
||||
storage_iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
|
||||
index -= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return iterator{instances, index-value};
|
||||
storage_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
||||
storage_iterator copy = *this;
|
||||
return (copy += value);
|
||||
}
|
||||
|
||||
iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
|
||||
storage_iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
|
||||
return (*this += -value);
|
||||
}
|
||||
|
||||
iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
||||
storage_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return (*this + -value);
|
||||
}
|
||||
|
||||
difference_type operator-(const iterator &other) const ENTT_NOEXCEPT {
|
||||
difference_type operator-(const storage_iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.index - index;
|
||||
}
|
||||
|
||||
reference operator[](const difference_type value) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
|
||||
const auto pos = size_type(index-value-1);
|
||||
return (*instances)[pos];
|
||||
}
|
||||
|
||||
bool operator==(const iterator &other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool operator==(const storage_iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.index == index;
|
||||
}
|
||||
|
||||
bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool operator!=(const storage_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const iterator &other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool operator<(const storage_iterator &other) const ENTT_NOEXCEPT {
|
||||
return index > other.index;
|
||||
}
|
||||
|
||||
bool operator>(const iterator &other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool operator>(const storage_iterator &other) const ENTT_NOEXCEPT {
|
||||
return index < other.index;
|
||||
}
|
||||
|
||||
bool operator<=(const iterator &other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool operator<=(const storage_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this > other);
|
||||
}
|
||||
|
||||
bool operator>=(const iterator &other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool operator>=(const storage_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this < other);
|
||||
}
|
||||
|
||||
pointer operator->() const ENTT_NOEXCEPT {
|
||||
const auto pos = size_type(index-1);
|
||||
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
|
||||
const auto pos = size_type(index-1u);
|
||||
return &(*instances)[pos];
|
||||
}
|
||||
|
||||
reference operator*() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
@@ -158,13 +159,18 @@ public:
|
||||
/*! @brief Type of the objects associated with the entities. */
|
||||
using object_type = Type;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename underlying_type::entity_type;
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename underlying_type::size_type;
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator_type = iterator<false>;
|
||||
using iterator = storage_iterator<false>;
|
||||
/*! @brief Constant random access iterator type. */
|
||||
using const_iterator_type = iterator<true>;
|
||||
using const_iterator = storage_iterator<true>;
|
||||
/*! @brief Reverse iterator type. */
|
||||
using reverse_iterator = Type *;
|
||||
/*! @brief Constant reverse iterator type. */
|
||||
using const_reverse_iterator = const Type *;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Increases the capacity of a storage.
|
||||
@@ -192,77 +198,113 @@ public:
|
||||
* always a valid range, even if the container is empty.
|
||||
*
|
||||
* @note
|
||||
* There are no guarantees on the order, even though either `sort` or
|
||||
* `respect` has been previously invoked. Internal data structures arrange
|
||||
* elements to maximize performance. Accessing them directly gives a
|
||||
* performance boost but less guarantees. Use `begin` and `end` if you want
|
||||
* to iterate the storage in the expected order.
|
||||
* Objects are in the reverse order as returned by the `begin`/`end`
|
||||
* iterators.
|
||||
*
|
||||
* @return A pointer to the array of objects.
|
||||
*/
|
||||
const object_type * raw() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] const object_type * raw() const ENTT_NOEXCEPT {
|
||||
return instances.data();
|
||||
}
|
||||
|
||||
/*! @copydoc raw */
|
||||
object_type * raw() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] object_type * raw() ENTT_NOEXCEPT {
|
||||
return const_cast<object_type *>(std::as_const(*this).raw());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
*
|
||||
* The returned iterator points to the first instance of the given type. If
|
||||
* the storage is empty, the returned iterator will be equal to `end()`.
|
||||
* The returned iterator points to the first instance of the internal array.
|
||||
* If the storage is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @note
|
||||
* Random access iterators stay true to the order imposed by a call to
|
||||
* either `sort` or `respect`.
|
||||
*
|
||||
* @return An iterator to the first instance of the given type.
|
||||
* @return An iterator to the first instance of the internal array.
|
||||
*/
|
||||
const_iterator_type cbegin() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
|
||||
const typename traits_type::difference_type pos = underlying_type::size();
|
||||
return const_iterator_type{&instances, pos};
|
||||
return const_iterator{instances, pos};
|
||||
}
|
||||
|
||||
/*! @copydoc cbegin */
|
||||
const_iterator_type begin() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/*! @copydoc begin */
|
||||
iterator_type begin() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
|
||||
const typename traits_type::difference_type pos = underlying_type::size();
|
||||
return iterator_type{&instances, pos};
|
||||
return iterator{instances, pos};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end.
|
||||
*
|
||||
* The returned iterator points to the element following the last instance
|
||||
* of the given type. Attempting to dereference the returned iterator
|
||||
* of the internal array. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Random access iterators stay true to the order imposed by a call to
|
||||
* either `sort` or `respect`.
|
||||
*
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* given type.
|
||||
* internal array.
|
||||
*/
|
||||
const_iterator_type cend() const ENTT_NOEXCEPT {
|
||||
return const_iterator_type{&instances, {}};
|
||||
[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
|
||||
return const_iterator{instances, {}};
|
||||
}
|
||||
|
||||
/*! @copydoc cend */
|
||||
const_iterator_type end() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/*! @copydoc end */
|
||||
iterator_type end() ENTT_NOEXCEPT {
|
||||
return iterator_type{&instances, {}};
|
||||
[[nodiscard]] iterator end() ENTT_NOEXCEPT {
|
||||
return iterator{instances, {}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reverse iterator to the beginning.
|
||||
*
|
||||
* The returned iterator points to the first instance of the reversed
|
||||
* internal array. If the storage is empty, the returned iterator will be
|
||||
* equal to `rend()`.
|
||||
*
|
||||
* @return An iterator to the first instance of the reversed internal array.
|
||||
*/
|
||||
[[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT {
|
||||
return instances.data();
|
||||
}
|
||||
|
||||
/*! @copydoc crbegin */
|
||||
[[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT {
|
||||
return crbegin();
|
||||
}
|
||||
|
||||
/*! @copydoc rbegin */
|
||||
[[nodiscard]] reverse_iterator rbegin() ENTT_NOEXCEPT {
|
||||
return instances.data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reverse iterator to the end.
|
||||
*
|
||||
* The returned iterator points to the element following the last instance
|
||||
* of the reversed internal array. Attempting to dereference the returned
|
||||
* iterator results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* reversed internal array.
|
||||
*/
|
||||
[[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT {
|
||||
return crbegin() + instances.size();
|
||||
}
|
||||
|
||||
/*! @copydoc crend */
|
||||
[[nodiscard]] const_reverse_iterator rend() const ENTT_NOEXCEPT {
|
||||
return crend();
|
||||
}
|
||||
|
||||
/*! @copydoc rend */
|
||||
[[nodiscard]] reverse_iterator rend() ENTT_NOEXCEPT {
|
||||
return rbegin() + instances.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -277,12 +319,12 @@ public:
|
||||
* @param entt A valid entity identifier.
|
||||
* @return The object associated with the entity.
|
||||
*/
|
||||
const object_type & get(const entity_type entt) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] const object_type & get(const entity_type entt) const {
|
||||
return instances[underlying_type::index(entt)];
|
||||
}
|
||||
|
||||
/*! @copydoc get */
|
||||
object_type & get(const entity_type entt) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] object_type & get(const entity_type entt) {
|
||||
return const_cast<object_type &>(std::as_const(*this).get(entt));
|
||||
}
|
||||
|
||||
@@ -291,12 +333,12 @@ public:
|
||||
* @param entt A valid entity identifier.
|
||||
* @return The object associated with the entity, if any.
|
||||
*/
|
||||
const object_type * try_get(const entity_type entt) const ENTT_NOEXCEPT {
|
||||
return underlying_type::has(entt) ? (instances.data() + underlying_type::index(entt)) : nullptr;
|
||||
[[nodiscard]] const object_type * try_get(const entity_type entt) const {
|
||||
return underlying_type::contains(entt) ? (instances.data() + underlying_type::index(entt)) : nullptr;
|
||||
}
|
||||
|
||||
/*! @copydoc try_get */
|
||||
object_type * try_get(const entity_type entt) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] object_type * try_get(const entity_type entt) {
|
||||
return const_cast<object_type *>(std::as_const(*this).try_get(entt));
|
||||
}
|
||||
|
||||
@@ -316,26 +358,22 @@ public:
|
||||
* @tparam Args Types of arguments to use to construct the object.
|
||||
* @param entt A valid entity identifier.
|
||||
* @param args Parameters to use to construct an object for the entity.
|
||||
* @return The object associated with the entity.
|
||||
*/
|
||||
template<typename... Args>
|
||||
object_type & construct(const entity_type entt, Args &&... args) {
|
||||
void emplace(const entity_type entt, Args &&... args) {
|
||||
if constexpr(std::is_aggregate_v<object_type>) {
|
||||
instances.emplace_back(Type{std::forward<Args>(args)...});
|
||||
instances.push_back(Type{std::forward<Args>(args)...});
|
||||
} else {
|
||||
instances.emplace_back(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// entity goes after component in case constructor throws
|
||||
underlying_type::construct(entt);
|
||||
return instances.back();
|
||||
underlying_type::emplace(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns one or more entities to a storage and default constructs
|
||||
* their objects.
|
||||
*
|
||||
* The object type must be at least move and default insertable.
|
||||
* @brief Assigns one or more entities to a storage and constructs their
|
||||
* objects from a given instance.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to assign an entity that already belongs to the storage
|
||||
@@ -343,41 +381,36 @@ public:
|
||||
* An assertion will abort the execution at runtime in debug mode if the
|
||||
* storage already contains the given entity.
|
||||
*
|
||||
* @tparam It Type of forward iterator.
|
||||
* @tparam It Type of input iterator.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
* @return An iterator to the list of instances just created and sorted the
|
||||
* same of the entities.
|
||||
* @param value An instance of the object to construct.
|
||||
*/
|
||||
template<typename It>
|
||||
iterator_type batch(It first, It last) {
|
||||
instances.resize(instances.size() + std::distance(first, last));
|
||||
// entity goes after component in case constructor throws
|
||||
underlying_type::batch(first, last);
|
||||
return begin();
|
||||
void insert(It first, It last, const object_type &value = {}) {
|
||||
instances.insert(instances.end(), std::distance(first, last), value);
|
||||
// entities go after components in case constructors throw
|
||||
underlying_type::insert(first, last);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns one or more entities to a storage and copy constructs
|
||||
* their objects.
|
||||
* @brief Assigns one or more entities to a storage and constructs their
|
||||
* objects from a given range.
|
||||
*
|
||||
* The object type must be at least move and copy insertable.
|
||||
* @sa construct
|
||||
*
|
||||
* @sa batch
|
||||
*
|
||||
* @tparam It Type of forward iterator.
|
||||
* @tparam EIt Type of input iterator.
|
||||
* @tparam CIt Type of input iterator.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
* @param value The value to initialize the new objects with.
|
||||
* @return An iterator to the list of instances just created and sorted the
|
||||
* same of the entities.
|
||||
* @param from An iterator to the first element of the range of objects.
|
||||
* @param to An iterator past the last element of the range of objects.
|
||||
*/
|
||||
template<typename It>
|
||||
iterator_type batch(It first, It last, const object_type &value) {
|
||||
instances.resize(instances.size() + std::distance(first, last), value);
|
||||
// entity goes after component in case constructor throws
|
||||
underlying_type::batch(first, last);
|
||||
return begin();
|
||||
template<typename EIt, typename CIt>
|
||||
void insert(EIt first, EIt last, CIt from, CIt to) {
|
||||
instances.insert(instances.end(), from, to);
|
||||
// entities go after components in case constructors throw
|
||||
underlying_type::insert(first, last);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -391,11 +424,11 @@ public:
|
||||
*
|
||||
* @param entt A valid entity identifier.
|
||||
*/
|
||||
void destroy(const entity_type entt) {
|
||||
void erase(const entity_type entt) {
|
||||
auto other = std::move(instances.back());
|
||||
instances[underlying_type::index(entt)] = std::move(other);
|
||||
instances.pop_back();
|
||||
underlying_type::destroy(entt);
|
||||
underlying_type::erase(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -407,13 +440,11 @@ public:
|
||||
* An assertion will abort the execution at runtime in debug mode if the
|
||||
* sparse set doesn't contain the given entities.
|
||||
*
|
||||
* @param lhs A valid position within the sparse set.
|
||||
* @param rhs A valid position within the sparse set.
|
||||
* @param lhs A valid entity identifier.
|
||||
* @param rhs A valid entity identifier.
|
||||
*/
|
||||
void swap(const size_type lhs, const size_type rhs) ENTT_NOEXCEPT override {
|
||||
ENTT_ASSERT(lhs < instances.size());
|
||||
ENTT_ASSERT(rhs < instances.size());
|
||||
std::swap(instances[lhs], instances[rhs]);
|
||||
void swap(const entity_type lhs, const entity_type rhs) override {
|
||||
std::swap(instances[underlying_type::index(lhs)], instances[underlying_type::index(rhs)]);
|
||||
underlying_type::swap(lhs, rhs);
|
||||
}
|
||||
|
||||
@@ -443,14 +474,10 @@ public:
|
||||
* * An iterator past the last element of the range to sort.
|
||||
* * A comparison function to use to compare the elements.
|
||||
*
|
||||
* The comparison function object received by the sort function object
|
||||
* hasn't necessarily the type of the one passed along with the other
|
||||
* parameters to this member function.
|
||||
*
|
||||
* @note
|
||||
* Attempting to iterate elements using a raw pointer returned by a call to
|
||||
* either `data` or `raw` gives no guarantees on the order, even though
|
||||
* `sort` has been invoked.
|
||||
* @warning
|
||||
* Empty types are never instantiated. Therefore, only comparison function
|
||||
* objects that require to return entities rather than components are
|
||||
* accepted.
|
||||
*
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Sort Type of sort function object.
|
||||
@@ -462,26 +489,29 @@ public:
|
||||
* @param args Arguments to forward to the sort function object, if any.
|
||||
*/
|
||||
template<typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(iterator_type first, iterator_type last, Compare compare, Sort algo = Sort{}, Args &&... args) {
|
||||
ENTT_ASSERT(!(first > last));
|
||||
void sort(iterator first, iterator last, Compare compare, Sort algo = Sort{}, Args &&... args) {
|
||||
ENTT_ASSERT(!(last < first));
|
||||
ENTT_ASSERT(!(last > end()));
|
||||
|
||||
const auto from = underlying_type::begin() + std::distance(begin(), first);
|
||||
const auto to = from + std::distance(first, last);
|
||||
|
||||
if constexpr(std::is_invocable_v<Compare, const object_type &, const object_type &>) {
|
||||
static_assert(!std::is_empty_v<object_type>);
|
||||
const auto apply = [this](const auto lhs, const auto rhs) {
|
||||
std::swap(instances[underlying_type::index(lhs)], instances[underlying_type::index(rhs)]);
|
||||
};
|
||||
|
||||
underlying_type::sort(from, to, [this, compare = std::move(compare)](const auto lhs, const auto rhs) {
|
||||
if constexpr(std::is_invocable_v<Compare, const object_type &, const object_type &>) {
|
||||
underlying_type::arrange(from, to, std::move(apply), [this, compare = std::move(compare)](const auto lhs, const auto rhs) {
|
||||
return compare(std::as_const(instances[underlying_type::index(lhs)]), std::as_const(instances[underlying_type::index(rhs)]));
|
||||
}, std::move(algo), std::forward<Args>(args)...);
|
||||
} else {
|
||||
underlying_type::sort(from, to, std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
underlying_type::arrange(from, to, std::move(apply), std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
/*! @brief Resets a storage. */
|
||||
void reset() {
|
||||
underlying_type::reset();
|
||||
/*! @brief Clears a storage. */
|
||||
void clear() {
|
||||
underlying_type::clear();
|
||||
instances.clear();
|
||||
}
|
||||
|
||||
@@ -490,215 +520,59 @@ private:
|
||||
};
|
||||
|
||||
|
||||
/*! @copydoc basic_storage */
|
||||
/*! @copydoc storage */
|
||||
template<typename Entity, typename Type>
|
||||
class basic_storage<Entity, Type, std::enable_if_t<std::is_empty_v<Type>>>: public sparse_set<Entity> {
|
||||
using traits_type = entt_traits<std::underlying_type_t<Entity>>;
|
||||
class storage<Entity, Type, std::enable_if_t<is_eto_eligible_v<Type>>>: public sparse_set<Entity> {
|
||||
using underlying_type = sparse_set<Entity>;
|
||||
|
||||
class iterator {
|
||||
friend class basic_storage<Entity, Type>;
|
||||
|
||||
using index_type = typename traits_type::difference_type;
|
||||
|
||||
iterator(const index_type idx) ENTT_NOEXCEPT
|
||||
: index{idx}
|
||||
{}
|
||||
|
||||
public:
|
||||
using difference_type = index_type;
|
||||
using value_type = Type;
|
||||
using pointer = const value_type *;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
iterator & operator++() ENTT_NOEXCEPT {
|
||||
return --index, *this;
|
||||
}
|
||||
|
||||
iterator operator++(int) ENTT_NOEXCEPT {
|
||||
iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
iterator & operator--() ENTT_NOEXCEPT {
|
||||
return ++index, *this;
|
||||
}
|
||||
|
||||
iterator operator--(int) ENTT_NOEXCEPT {
|
||||
iterator orig = *this;
|
||||
return --(*this), orig;
|
||||
}
|
||||
|
||||
iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
|
||||
index -= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return iterator{index-value};
|
||||
}
|
||||
|
||||
iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
|
||||
return (*this += -value);
|
||||
}
|
||||
|
||||
iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return (*this + -value);
|
||||
}
|
||||
|
||||
difference_type operator-(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.index - index;
|
||||
}
|
||||
|
||||
reference operator[](const difference_type) const ENTT_NOEXCEPT {
|
||||
return {};
|
||||
}
|
||||
|
||||
bool operator==(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.index == index;
|
||||
}
|
||||
|
||||
bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return index > other.index;
|
||||
}
|
||||
|
||||
bool operator>(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return index < other.index;
|
||||
}
|
||||
|
||||
bool operator<=(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this > other);
|
||||
}
|
||||
|
||||
bool operator>=(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this < other);
|
||||
}
|
||||
|
||||
pointer operator->() const ENTT_NOEXCEPT {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
reference operator*() const ENTT_NOEXCEPT {
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
index_type index;
|
||||
};
|
||||
|
||||
public:
|
||||
/*! @brief Type of the objects associated with the entities. */
|
||||
using object_type = Type;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename underlying_type::entity_type;
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename underlying_type::size_type;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator_type = iterator;
|
||||
using size_type = std::size_t;
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
*
|
||||
* The returned iterator points to the first instance of the given type. If
|
||||
* the storage is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed by a call to either `sort`
|
||||
* or `respect`.
|
||||
*
|
||||
* @return An iterator to the first instance of the given type.
|
||||
*/
|
||||
iterator_type cbegin() const ENTT_NOEXCEPT {
|
||||
const typename traits_type::difference_type pos = underlying_type::size();
|
||||
return iterator_type{pos};
|
||||
}
|
||||
|
||||
/*! @copydoc cbegin */
|
||||
iterator_type begin() const ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end.
|
||||
*
|
||||
* The returned iterator points to the element following the last instance
|
||||
* of the given type. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed by a call to either `sort`
|
||||
* or `respect`.
|
||||
*
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* given type.
|
||||
*/
|
||||
iterator_type cend() const ENTT_NOEXCEPT {
|
||||
return iterator_type{};
|
||||
}
|
||||
|
||||
/*! @copydoc cend */
|
||||
iterator_type end() const ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the object associated with an entity.
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated. Therefore, this function
|
||||
* always returns a temporary object.
|
||||
* @brief Assigns an entity to a storage and constructs its object.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an entity that doesn't belong to the storage results in
|
||||
* undefined behavior.<br/>
|
||||
* Attempting to use an entity that already belongs to the storage results
|
||||
* in undefined behavior.<br/>
|
||||
* An assertion will abort the execution at runtime in debug mode if the
|
||||
* storage doesn't contain the given entity.
|
||||
* storage already contains the given entity.
|
||||
*
|
||||
* @tparam Args Types of arguments to use to construct the object.
|
||||
* @param entt A valid entity identifier.
|
||||
* @return The object associated with the entity.
|
||||
* @param args Parameters to use to construct an object for the entity.
|
||||
*/
|
||||
object_type get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(underlying_type::has(entt));
|
||||
return {};
|
||||
template<typename... Args>
|
||||
void emplace(const entity_type entt, Args &&... args) {
|
||||
[[maybe_unused]] object_type instance{std::forward<Args>(args)...};
|
||||
underlying_type::emplace(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns one or more entities to a storage.
|
||||
*
|
||||
* The object type must be at least default constructible.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to assign an entity that already belongs to the storage
|
||||
* results in undefined behavior.<br/>
|
||||
* An assertion will abort the execution at runtime in debug mode if the
|
||||
* storage already contains the given entity.
|
||||
*
|
||||
* @tparam It Type of forward iterator.
|
||||
* @tparam It Type of input iterator.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
* @return An iterator to the list of instances just created and sorted the
|
||||
* same of the entities.
|
||||
*/
|
||||
template<typename It>
|
||||
iterator_type batch(It first, It last, const object_type & = {}) {
|
||||
underlying_type::batch(first, last);
|
||||
return begin();
|
||||
void insert(It first, It last, const object_type & = {}) {
|
||||
underlying_type::insert(first, last);
|
||||
}
|
||||
};
|
||||
|
||||
/*! @copydoc basic_storage */
|
||||
template<typename Entity, typename Type>
|
||||
struct storage: basic_storage<Entity, Type> {};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_ENTITY_STORAGE_HPP
|
||||
#endif
|
||||
|
||||
@@ -21,7 +21,7 @@ struct exclude_t: type_list<Type...> {};
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
constexpr exclude_t<Type...> exclude{};
|
||||
inline constexpr exclude_t<Type...> exclude{};
|
||||
|
||||
|
||||
/**
|
||||
@@ -37,10 +37,10 @@ struct get_t: type_list<Type...>{};
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
constexpr get_t<Type...> get{};
|
||||
inline constexpr get_t<Type...> get{};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_ENTITY_UTILITY_HPP
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,19 @@
|
||||
#include "core/algorithm.hpp"
|
||||
#include "core/attribute.h"
|
||||
#include "core/family.hpp"
|
||||
#include "core/hashed_string.hpp"
|
||||
#include "core/ident.hpp"
|
||||
#include "core/monostate.hpp"
|
||||
#include "core/type_info.hpp"
|
||||
#include "core/type_traits.hpp"
|
||||
#include "core/utility.hpp"
|
||||
#include "entity/actor.hpp"
|
||||
#include "entity/entity.hpp"
|
||||
#include "entity/group.hpp"
|
||||
#include "entity/handle.hpp"
|
||||
#include "entity/helper.hpp"
|
||||
#include "entity/observer.hpp"
|
||||
#include "entity/pool.hpp"
|
||||
#include "entity/registry.hpp"
|
||||
#include "entity/runtime_view.hpp"
|
||||
#include "entity/snapshot.hpp"
|
||||
@@ -18,9 +22,17 @@
|
||||
#include "entity/utility.hpp"
|
||||
#include "entity/view.hpp"
|
||||
#include "locator/locator.hpp"
|
||||
#include "meta/container.hpp"
|
||||
#include "meta/ctx.hpp"
|
||||
#include "meta/factory.hpp"
|
||||
#include "meta/internal.hpp"
|
||||
#include "meta/meta.hpp"
|
||||
#include "meta/pointer.hpp"
|
||||
#include "meta/policy.hpp"
|
||||
#include "meta/range.hpp"
|
||||
#include "meta/resolve.hpp"
|
||||
#include "meta/type_traits.hpp"
|
||||
#include "platform/android-ndk-r17.hpp"
|
||||
#include "process/process.hpp"
|
||||
#include "process/scheduler.hpp"
|
||||
#include "resource/cache.hpp"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "core/fwd.hpp"
|
||||
#include "entity/fwd.hpp"
|
||||
#include "resource/fwd.hpp"
|
||||
#include "signal/fwd.hpp"
|
||||
|
||||
@@ -35,7 +35,7 @@ struct service_locator {
|
||||
* @brief Tests if a valid service implementation is set.
|
||||
* @return True if the service is set, false otherwise.
|
||||
*/
|
||||
static bool empty() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] static bool empty() ENTT_NOEXCEPT {
|
||||
return !static_cast<bool>(service);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ struct service_locator {
|
||||
*
|
||||
* @return A reference to the service implementation currently set, if any.
|
||||
*/
|
||||
static std::weak_ptr<Service> get() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] static std::weak_ptr<Service> get() ENTT_NOEXCEPT {
|
||||
return service;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ struct service_locator {
|
||||
*
|
||||
* @return A reference to the service implementation currently set, if any.
|
||||
*/
|
||||
static Service & ref() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] static Service & ref() ENTT_NOEXCEPT {
|
||||
return *service;
|
||||
}
|
||||
|
||||
@@ -108,4 +108,4 @@ private:
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_LOCATOR_LOCATOR_HPP
|
||||
#endif
|
||||
|
||||
387
src/entt/meta/container.hpp
Normal file
387
src/entt/meta/container.hpp
Normal file
@@ -0,0 +1,387 @@
|
||||
#ifndef ENTT_META_CONTAINER_HPP
|
||||
#define ENTT_META_CONTAINER_HPP
|
||||
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "type_traits.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
namespace internal {
|
||||
|
||||
|
||||
template<typename Container, template<typename> class... Trait>
|
||||
struct container_traits: public Trait<Container>... {};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Basic STL-compatible container traits
|
||||
* @tparam Container The type of the container.
|
||||
*/
|
||||
template<typename Container>
|
||||
struct basic_container {
|
||||
/*! @brief Iterator type of the container. */
|
||||
using iterator = typename Container::iterator;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename Container::size_type;
|
||||
/*! @brief Value type of the container. */
|
||||
using value_type = typename Container::value_type;
|
||||
|
||||
/**
|
||||
* @brief Returns the size of the given container.
|
||||
* @param cont The container for which to return the size.
|
||||
* @return The size of the given container.
|
||||
*/
|
||||
[[nodiscard]] static size_type size(const Container &cont) ENTT_NOEXCEPT {
|
||||
return cont.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first element of the given container.
|
||||
* @param cont The container for which to return the iterator.
|
||||
* @return An iterator to the first element of the given container.
|
||||
*/
|
||||
[[nodiscard]] static iterator begin(Container &cont) {
|
||||
return cont.begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator past the last element of the given container.
|
||||
* @param cont The container for which to return the iterator.
|
||||
* @return An iterator past the last element of the given container.
|
||||
*/
|
||||
[[nodiscard]] static iterator end(Container &cont) {
|
||||
return cont.end();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Basic STL-compatible associative container traits
|
||||
* @tparam Container The type of the container.
|
||||
*/
|
||||
template<typename Container>
|
||||
struct basic_associative_container {
|
||||
/*! @brief Key type of the sequence container. */
|
||||
using key_type = typename Container::key_type;
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the element with key equivalent to the given
|
||||
* one, if any.
|
||||
* @param cont The container in which to search for the element.
|
||||
* @param key The key of the element to search.
|
||||
* @return An iterator to the element with the given key, if any.
|
||||
*/
|
||||
[[nodiscard]] static typename Container::iterator find(Container &cont, const key_type &key) {
|
||||
return cont.find(key);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Basic STL-compatible dynamic container traits
|
||||
* @tparam Container The type of the container.
|
||||
*/
|
||||
template<typename Container>
|
||||
struct basic_dynamic_container {
|
||||
/**
|
||||
* @brief Clears the content of the given container.
|
||||
* @param cont The container for which to clear the content.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] static bool clear(Container &cont) {
|
||||
return cont.clear(), true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Basic STL-compatible dynamic associative container traits
|
||||
* @tparam Container The type of the container.
|
||||
*/
|
||||
template<typename Container>
|
||||
struct basic_dynamic_associative_container {
|
||||
/**
|
||||
* @brief Removes the specified element from the given container.
|
||||
* @param cont The container from which to remove the element.
|
||||
* @param key The element to remove.
|
||||
* @return A bool denoting whether the removal took place.
|
||||
*/
|
||||
[[nodiscard]] static bool erase(Container &cont, const typename Container::key_type &key) {
|
||||
const auto sz = cont.size();
|
||||
return cont.erase(key) != sz;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Basic STL-compatible sequence container traits
|
||||
* @tparam Container The type of the container.
|
||||
*/
|
||||
template<typename Container>
|
||||
struct basic_sequence_container {
|
||||
/**
|
||||
* @brief Returns a reference to the element at the specified location of the
|
||||
* given container (no bounds checking is performed).
|
||||
* @param cont The container from which to get the element.
|
||||
* @param pos The position of the element to return.
|
||||
* @return A reference to the requested element.
|
||||
*/
|
||||
[[nodiscard]] static typename Container::value_type & get(Container &cont, typename Container::size_type pos) {
|
||||
return cont[pos];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief STL-compatible dynamic associative key-only container traits
|
||||
* @tparam Container The type of the container.
|
||||
*/
|
||||
template<typename Container>
|
||||
struct dynamic_associative_key_only_container {
|
||||
/**
|
||||
* @brief Inserts an element into the given container.
|
||||
* @param cont The container in which to insert the element.
|
||||
* @param key The element to insert.
|
||||
* @return A bool denoting whether the insertion took place.
|
||||
*/
|
||||
[[nodiscard]] static bool insert(Container &cont, const typename Container::key_type &key) {
|
||||
return cont.insert(key).second;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief STL-compatible dynamic key-value associative container traits
|
||||
* @tparam Container The type of the container.
|
||||
*/
|
||||
template<typename Container>
|
||||
struct dynamic_associative_key_value_container {
|
||||
/**
|
||||
* @brief Inserts an element (a key/value pair) into the given container.
|
||||
* @param cont The container in which to insert the element.
|
||||
* @param key The key of the element to insert.
|
||||
* @param value The value of the element to insert.
|
||||
* @return A bool denoting whether the insertion took place.
|
||||
*/
|
||||
[[nodiscard]] static bool insert(Container &cont, const typename Container::key_type &key, const typename Container::mapped_type &value) {
|
||||
return cont.insert(std::make_pair(key, value)).second;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief STL-compatible dynamic sequence container traits
|
||||
* @tparam Container The type of the container.
|
||||
*/
|
||||
template<typename Container>
|
||||
struct dynamic_sequence_container {
|
||||
/**
|
||||
* @brief Resizes the given container to contain the given number of elements.
|
||||
* @param cont The container to resize.
|
||||
* @param sz The new size of the container.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] static bool resize(Container &cont, typename Container::size_type sz) {
|
||||
return (cont.resize(sz), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts an element at the specified location of the given container.
|
||||
* @param cont The container into which to insert the element.
|
||||
* @param it Iterator before which the element will be inserted.
|
||||
* @param value Element value to insert.
|
||||
* @return A pair consisting of an iterator to the inserted element (in case
|
||||
* of success) and a bool denoting whether the insertion took place.
|
||||
*/
|
||||
[[nodiscard]] static std::pair<typename Container::iterator, bool> insert(Container &cont, typename Container::iterator it, const typename Container::value_type &value) {
|
||||
return { cont.insert(it, value), true };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the element at the specified location from the given container.
|
||||
* @param cont The container from which to remove the element.
|
||||
* @param it Iterator to the element to remove.
|
||||
* @return A pair consisting of an iterator following the last removed
|
||||
* element (in case of success) and a bool denoting whether the insertion
|
||||
* took place.
|
||||
*/
|
||||
[[nodiscard]] static std::pair<typename Container::iterator, bool> erase(Container &cont, typename Container::iterator it) {
|
||||
return { cont.erase(it), true };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief STL-compatible fixed sequence container traits
|
||||
* @tparam Container The type of the container.
|
||||
*/
|
||||
template<typename Container>
|
||||
struct fixed_sequence_container {
|
||||
/**
|
||||
* @brief Does nothing.
|
||||
* @return False to indicate failure in all cases.
|
||||
*/
|
||||
[[nodiscard]] static bool resize(const Container &, typename Container::size_type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Does nothing.
|
||||
* @return False to indicate failure in all cases.
|
||||
*/
|
||||
[[nodiscard]] static bool clear(const Container &) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Does nothing.
|
||||
* @return A pair consisting of an invalid iterator and a false value to
|
||||
* indicate failure in all cases.
|
||||
*/
|
||||
[[nodiscard]] static std::pair<typename Container::iterator, bool> insert(const Container &, typename Container::iterator, const typename Container::value_type &) {
|
||||
return { {}, false };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Does nothing.
|
||||
* @return A pair consisting of an invalid iterator and a false value to
|
||||
* indicate failure in all cases.
|
||||
*/
|
||||
[[nodiscard]] static std::pair<typename Container::iterator, bool> erase(const Container &, typename Container::iterator) {
|
||||
return { {}, false };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Meta sequence container traits for `std::vector`s of any type.
|
||||
* @tparam Type The type of elements.
|
||||
* @tparam Args Other arguments.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
struct meta_sequence_container_traits<std::vector<Type, Args...>>
|
||||
: internal::container_traits<
|
||||
std::vector<Type, Args...>,
|
||||
internal::basic_container,
|
||||
internal::basic_dynamic_container,
|
||||
internal::basic_sequence_container,
|
||||
internal::dynamic_sequence_container
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Meta sequence container traits for `std::array`s of any type.
|
||||
* @tparam Type The type of elements.
|
||||
* @tparam N The number of elements.
|
||||
*/
|
||||
template<typename Type, auto N>
|
||||
struct meta_sequence_container_traits<std::array<Type, N>>
|
||||
: internal::container_traits<
|
||||
std::array<Type, N>,
|
||||
internal::basic_container,
|
||||
internal::basic_sequence_container,
|
||||
internal::fixed_sequence_container
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Meta associative container traits for `std::map`s of any type.
|
||||
* @tparam Key The key type of elements.
|
||||
* @tparam Value The value type of elements.
|
||||
* @tparam Args Other arguments.
|
||||
*/
|
||||
template<typename Key, typename Value, typename... Args>
|
||||
struct meta_associative_container_traits<std::map<Key, Value, Args...>>
|
||||
: internal::container_traits<
|
||||
std::map<Key, Value, Args...>,
|
||||
internal::basic_container,
|
||||
internal::basic_associative_container,
|
||||
internal::basic_dynamic_container,
|
||||
internal::basic_dynamic_associative_container,
|
||||
internal::dynamic_associative_key_value_container
|
||||
>
|
||||
{
|
||||
/*! @brief Mapped type of the sequence container. */
|
||||
using mapped_type = typename std::map<Key, Value, Args...>::mapped_type;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Meta associative container traits for `std::unordered_map`s of any
|
||||
* type.
|
||||
* @tparam Key The key type of elements.
|
||||
* @tparam Value The value type of elements.
|
||||
* @tparam Args Other arguments.
|
||||
*/
|
||||
template<typename Key, typename Value, typename... Args>
|
||||
struct meta_associative_container_traits<std::unordered_map<Key, Value, Args...>>
|
||||
: internal::container_traits<
|
||||
std::unordered_map<Key, Value, Args...>,
|
||||
internal::basic_container,
|
||||
internal::basic_associative_container,
|
||||
internal::basic_dynamic_container,
|
||||
internal::basic_dynamic_associative_container,
|
||||
internal::dynamic_associative_key_value_container
|
||||
>
|
||||
{
|
||||
/*! @brief Mapped type of the sequence container. */
|
||||
using mapped_type = typename std::unordered_map<Key, Value, Args...>::mapped_type;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Meta associative container traits for `std::set`s of any type.
|
||||
* @tparam Key The type of elements.
|
||||
* @tparam Args Other arguments.
|
||||
*/
|
||||
template<typename Key, typename... Args>
|
||||
struct meta_associative_container_traits<std::set<Key, Args...>>
|
||||
: internal::container_traits<
|
||||
std::set<Key, Args...>,
|
||||
internal::basic_container,
|
||||
internal::basic_associative_container,
|
||||
internal::basic_dynamic_container,
|
||||
internal::basic_dynamic_associative_container,
|
||||
internal::dynamic_associative_key_only_container
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Meta associative container traits for `std::unordered_set`s of any
|
||||
* type.
|
||||
* @tparam Key The type of elements.
|
||||
* @tparam Args Other arguments.
|
||||
*/
|
||||
template<typename Key, typename... Args>
|
||||
struct meta_associative_container_traits<std::unordered_set<Key, Args...>>
|
||||
: internal::container_traits<
|
||||
std::unordered_set<Key, Args...>,
|
||||
internal::basic_container,
|
||||
internal::basic_associative_container,
|
||||
internal::basic_dynamic_container,
|
||||
internal::basic_dynamic_associative_container,
|
||||
internal::dynamic_associative_key_only_container
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
68
src/entt/meta/ctx.hpp
Normal file
68
src/entt/meta/ctx.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef ENTT_META_CTX_HPP
|
||||
#define ENTT_META_CTX_HPP
|
||||
|
||||
|
||||
#include "../core/attribute.h"
|
||||
#include "../config/config.h"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
|
||||
namespace internal {
|
||||
|
||||
|
||||
struct meta_type_node;
|
||||
|
||||
|
||||
struct ENTT_API meta_context {
|
||||
// we could use the lines below but VS2017 returns with an ICE if combined with ENTT_API despite the code being valid C++
|
||||
// inline static meta_type_node *local = nullptr;
|
||||
// inline static meta_type_node **global = &local;
|
||||
|
||||
[[nodiscard]] static meta_type_node * & local() ENTT_NOEXCEPT {
|
||||
static meta_type_node *chain = nullptr;
|
||||
return chain;
|
||||
}
|
||||
|
||||
[[nodiscard]] static meta_type_node ** & global() ENTT_NOEXCEPT {
|
||||
static meta_type_node **chain = &local();
|
||||
return chain;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
|
||||
/*! @brief Opaque container for a meta context. */
|
||||
struct meta_ctx {
|
||||
/**
|
||||
* @brief Binds the meta system to a given context.
|
||||
* @param other A valid context to which to bind.
|
||||
*/
|
||||
static void bind(meta_ctx other) ENTT_NOEXCEPT {
|
||||
internal::meta_context::global() = other.ctx;
|
||||
}
|
||||
|
||||
private:
|
||||
internal::meta_type_node **ctx{&internal::meta_context::local()};
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
455
src/entt/meta/internal.hpp
Normal file
455
src/entt/meta/internal.hpp
Normal file
@@ -0,0 +1,455 @@
|
||||
#ifndef ENTT_META_INTERNAL_HPP
|
||||
#define ENTT_META_INTERNAL_HPP
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../core/attribute.h"
|
||||
#include "../config/config.h"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "type_traits.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
class meta_any;
|
||||
struct meta_handle;
|
||||
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
|
||||
namespace internal {
|
||||
|
||||
|
||||
class meta_storage {
|
||||
using storage_type = std::aligned_storage_t<sizeof(void *), alignof(void *)>;
|
||||
using copy_fn_type = void(meta_storage &, const meta_storage &);
|
||||
using steal_fn_type = void(meta_storage &, meta_storage &);
|
||||
using destroy_fn_type = void(meta_storage &);
|
||||
|
||||
template<typename Type, typename = std::void_t<>>
|
||||
struct type_traits {
|
||||
template<typename... Args>
|
||||
static void instance(meta_storage &buffer, Args &&... args) {
|
||||
buffer.instance = new Type{std::forward<Args>(args)...};
|
||||
new (&buffer.storage) Type *{static_cast<Type *>(buffer.instance)};
|
||||
}
|
||||
|
||||
static void destroy(meta_storage &buffer) {
|
||||
delete static_cast<Type *>(buffer.instance);
|
||||
}
|
||||
|
||||
static void copy(meta_storage &to, const meta_storage &from) {
|
||||
to.instance = new Type{*static_cast<const Type *>(from.instance)};
|
||||
new (&to.storage) Type *{static_cast<Type *>(to.instance)};
|
||||
}
|
||||
|
||||
static void steal(meta_storage &to, meta_storage &from) {
|
||||
new (&to.storage) Type *{static_cast<Type *>(from.instance)};
|
||||
to.instance = from.instance;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
struct type_traits<Type, std::enable_if_t<sizeof(Type) <= sizeof(void *) && std::is_nothrow_move_constructible_v<Type>>> {
|
||||
template<typename... Args>
|
||||
static void instance(meta_storage &buffer, Args &&... args) {
|
||||
buffer.instance = new (&buffer.storage) Type{std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
static void destroy(meta_storage &buffer) {
|
||||
static_cast<Type *>(buffer.instance)->~Type();
|
||||
}
|
||||
|
||||
static void copy(meta_storage &to, const meta_storage &from) {
|
||||
to.instance = new (&to.storage) Type{*static_cast<const Type *>(from.instance)};
|
||||
}
|
||||
|
||||
static void steal(meta_storage &to, meta_storage &from) {
|
||||
to.instance = new (&to.storage) Type{std::move(*static_cast<Type *>(from.instance))};
|
||||
destroy(from);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
/*! @brief Default constructor. */
|
||||
meta_storage() ENTT_NOEXCEPT
|
||||
: storage{},
|
||||
instance{},
|
||||
destroy_fn{},
|
||||
copy_fn{},
|
||||
steal_fn{}
|
||||
{}
|
||||
|
||||
template<typename Type, typename... Args>
|
||||
explicit meta_storage(std::in_place_type_t<Type>, [[maybe_unused]] Args &&... args)
|
||||
: meta_storage{}
|
||||
{
|
||||
if constexpr(!std::is_void_v<Type>) {
|
||||
type_traits<Type>::instance(*this, std::forward<Args>(args)...);
|
||||
destroy_fn = &type_traits<Type>::destroy;
|
||||
copy_fn = &type_traits<Type>::copy;
|
||||
steal_fn = &type_traits<Type>::steal;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
meta_storage(std::reference_wrapper<Type> value)
|
||||
: meta_storage{}
|
||||
{
|
||||
instance = &value.get();
|
||||
}
|
||||
|
||||
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, meta_storage>>>
|
||||
meta_storage(Type &&value)
|
||||
: meta_storage{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)}
|
||||
{}
|
||||
|
||||
meta_storage(const meta_storage &other)
|
||||
: meta_storage{}
|
||||
{
|
||||
(other.copy_fn ? other.copy_fn : [](auto &to, const auto &from) { to.instance = from.instance; })(*this, other);
|
||||
destroy_fn = other.destroy_fn;
|
||||
copy_fn = other.copy_fn;
|
||||
steal_fn = other.steal_fn;
|
||||
}
|
||||
|
||||
meta_storage(meta_storage &&other)
|
||||
: meta_storage{}
|
||||
{
|
||||
swap(*this, other);
|
||||
}
|
||||
|
||||
~meta_storage() {
|
||||
if(destroy_fn) {
|
||||
destroy_fn(*this);
|
||||
}
|
||||
}
|
||||
|
||||
meta_storage & operator=(meta_storage other) {
|
||||
swap(other, *this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] const void * data() const ENTT_NOEXCEPT {
|
||||
return instance;
|
||||
}
|
||||
|
||||
[[nodiscard]] void * data() ENTT_NOEXCEPT {
|
||||
return const_cast<void *>(std::as_const(*this).data());
|
||||
}
|
||||
|
||||
template<typename Type, typename... Args>
|
||||
void emplace(Args &&... args) {
|
||||
*this = meta_storage{std::in_place_type<Type>, std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
[[nodiscard]] meta_storage ref() const ENTT_NOEXCEPT {
|
||||
meta_storage other{};
|
||||
other.instance = instance;
|
||||
return other;
|
||||
}
|
||||
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return !(instance == nullptr);
|
||||
}
|
||||
|
||||
friend void swap(meta_storage &lhs, meta_storage &rhs) {
|
||||
using std::swap;
|
||||
|
||||
if(lhs.steal_fn && rhs.steal_fn) {
|
||||
meta_storage buffer{};
|
||||
lhs.steal_fn(buffer, lhs);
|
||||
rhs.steal_fn(lhs, rhs);
|
||||
lhs.steal_fn(rhs, buffer);
|
||||
} else if(lhs.steal_fn) {
|
||||
lhs.steal_fn(rhs, lhs);
|
||||
} else if(rhs.steal_fn) {
|
||||
rhs.steal_fn(lhs, rhs);
|
||||
} else {
|
||||
swap(lhs.instance, rhs.instance);
|
||||
}
|
||||
|
||||
swap(lhs.destroy_fn, rhs.destroy_fn);
|
||||
swap(lhs.copy_fn, rhs.copy_fn);
|
||||
swap(lhs.steal_fn, rhs.steal_fn);
|
||||
}
|
||||
|
||||
private:
|
||||
storage_type storage;
|
||||
void *instance;
|
||||
destroy_fn_type *destroy_fn;
|
||||
copy_fn_type *copy_fn;
|
||||
steal_fn_type *steal_fn;
|
||||
};
|
||||
|
||||
|
||||
struct meta_type_node;
|
||||
|
||||
|
||||
struct meta_prop_node {
|
||||
meta_prop_node * next;
|
||||
meta_any(* const key)();
|
||||
meta_any(* const value)();
|
||||
};
|
||||
|
||||
|
||||
struct meta_base_node {
|
||||
meta_type_node * const parent;
|
||||
meta_base_node * next;
|
||||
meta_type_node *(* const type)() ENTT_NOEXCEPT;
|
||||
const void *(* const cast)(const void *) ENTT_NOEXCEPT;
|
||||
};
|
||||
|
||||
|
||||
struct meta_conv_node {
|
||||
meta_type_node * const parent;
|
||||
meta_conv_node * next;
|
||||
meta_type_node *(* const type)() ENTT_NOEXCEPT;
|
||||
meta_any(* const conv)(const void *);
|
||||
};
|
||||
|
||||
|
||||
struct meta_ctor_node {
|
||||
using size_type = std::size_t;
|
||||
meta_type_node * const parent;
|
||||
meta_ctor_node * next;
|
||||
meta_prop_node * prop;
|
||||
const size_type size;
|
||||
meta_type_node *(* const arg)(size_type) ENTT_NOEXCEPT;
|
||||
meta_any(* const invoke)(meta_any * const);
|
||||
};
|
||||
|
||||
|
||||
struct meta_data_node {
|
||||
id_type id;
|
||||
meta_type_node * const parent;
|
||||
meta_data_node * next;
|
||||
meta_prop_node * prop;
|
||||
const bool is_static;
|
||||
meta_type_node *(* const type)() ENTT_NOEXCEPT;
|
||||
bool(* const set)(meta_handle, meta_any);
|
||||
meta_any(* const get)(meta_handle);
|
||||
};
|
||||
|
||||
|
||||
struct meta_func_node {
|
||||
using size_type = std::size_t;
|
||||
id_type id;
|
||||
meta_type_node * const parent;
|
||||
meta_func_node * next;
|
||||
meta_prop_node * prop;
|
||||
const size_type size;
|
||||
const bool is_const;
|
||||
const bool is_static;
|
||||
meta_type_node *(* const ret)() ENTT_NOEXCEPT;
|
||||
meta_type_node *(* const arg)(size_type) ENTT_NOEXCEPT;
|
||||
meta_any(* const invoke)(meta_handle, meta_any *);
|
||||
};
|
||||
|
||||
|
||||
struct meta_type_node {
|
||||
using size_type = std::size_t;
|
||||
const id_type type_id;
|
||||
id_type id;
|
||||
meta_type_node * next;
|
||||
meta_prop_node * prop;
|
||||
const bool is_void;
|
||||
const bool is_integral;
|
||||
const bool is_floating_point;
|
||||
const bool is_array;
|
||||
const bool is_enum;
|
||||
const bool is_union;
|
||||
const bool is_class;
|
||||
const bool is_pointer;
|
||||
const bool is_function_pointer;
|
||||
const bool is_member_object_pointer;
|
||||
const bool is_member_function_pointer;
|
||||
const bool is_pointer_like;
|
||||
const bool is_sequence_container;
|
||||
const bool is_associative_container;
|
||||
const size_type rank;
|
||||
size_type(* const extent)(size_type);
|
||||
bool(* const compare)(const void *, const void *);
|
||||
meta_type_node *(* const remove_pointer)() ENTT_NOEXCEPT;
|
||||
meta_type_node *(* const remove_extent)() ENTT_NOEXCEPT;
|
||||
meta_base_node *base{nullptr};
|
||||
meta_conv_node *conv{nullptr};
|
||||
meta_ctor_node *ctor{nullptr};
|
||||
meta_data_node *data{nullptr};
|
||||
meta_func_node *func{nullptr};
|
||||
void(* dtor)(void *){nullptr};
|
||||
};
|
||||
|
||||
|
||||
template<typename Node>
|
||||
class meta_range {
|
||||
struct range_iterator {
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = Node;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
range_iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
range_iterator(Node *head) ENTT_NOEXCEPT
|
||||
: node{head}
|
||||
{}
|
||||
|
||||
range_iterator & operator++() ENTT_NOEXCEPT {
|
||||
return node = node->next, *this;
|
||||
}
|
||||
|
||||
range_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
range_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const range_iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.node == node;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator!=(const range_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
|
||||
return node;
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
private:
|
||||
Node *node{nullptr};
|
||||
};
|
||||
|
||||
public:
|
||||
using iterator = range_iterator;
|
||||
|
||||
meta_range() ENTT_NOEXCEPT = default;
|
||||
|
||||
meta_range(Node *head)
|
||||
: node{head}
|
||||
{}
|
||||
|
||||
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
|
||||
return iterator{node};
|
||||
}
|
||||
|
||||
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
|
||||
return iterator{};
|
||||
}
|
||||
|
||||
private:
|
||||
Node *node{nullptr};
|
||||
};
|
||||
|
||||
|
||||
template<auto Member, typename Op>
|
||||
auto find_if(const Op &op, const meta_type_node *node)
|
||||
-> std::decay_t<decltype(node->*Member)> {
|
||||
std::decay_t<decltype(node->*Member)> ret = nullptr;
|
||||
|
||||
for(auto &&curr: meta_range{node->*Member}) {
|
||||
if(op(&curr)) {
|
||||
ret = &curr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!ret) {
|
||||
for(auto &&curr: meta_range{node->base}) {
|
||||
if(ret = find_if<Member>(op, curr.type()); ret) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
template<typename Type>
|
||||
class ENTT_API meta_node {
|
||||
static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Invalid type");
|
||||
|
||||
[[nodiscard]] static bool compare(const void *lhs, const void *rhs) {
|
||||
if constexpr(!std::is_function_v<Type> && is_equality_comparable_v<Type>) {
|
||||
return *static_cast<const Type *>(lhs) == *static_cast<const Type *>(rhs);
|
||||
} else {
|
||||
return lhs == rhs;
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t... Index>
|
||||
[[nodiscard]] static auto extent(meta_type_node::size_type dim, std::index_sequence<Index...>) {
|
||||
meta_type_node::size_type ext{};
|
||||
((ext = (dim == Index ? std::extent_v<Type, Index> : ext)), ...);
|
||||
return ext;
|
||||
}
|
||||
|
||||
public:
|
||||
[[nodiscard]] static meta_type_node * resolve() ENTT_NOEXCEPT {
|
||||
static meta_type_node node{
|
||||
type_info<Type>::id(),
|
||||
{},
|
||||
nullptr,
|
||||
nullptr,
|
||||
std::is_void_v<Type>,
|
||||
std::is_integral_v<Type>,
|
||||
std::is_floating_point_v<Type>,
|
||||
std::is_array_v<Type>,
|
||||
std::is_enum_v<Type>,
|
||||
std::is_union_v<Type>,
|
||||
std::is_class_v<Type>,
|
||||
std::is_pointer_v<Type>,
|
||||
std::is_pointer_v<Type> && std::is_function_v<std::remove_pointer_t<Type>>,
|
||||
std::is_member_object_pointer_v<Type>,
|
||||
std::is_member_function_pointer_v<Type>,
|
||||
is_meta_pointer_like_v<Type>,
|
||||
has_meta_sequence_container_traits_v<Type>,
|
||||
has_meta_associative_container_traits_v<Type>,
|
||||
std::rank_v<Type>,
|
||||
[](meta_type_node::size_type dim) {
|
||||
return extent(dim, std::make_index_sequence<std::rank_v<Type>>{});
|
||||
},
|
||||
&compare, // workaround for an issue with VS2017
|
||||
&meta_node<std::remove_const_t<std::remove_pointer_t<Type>>>::resolve,
|
||||
&meta_node<std::remove_const_t<std::remove_extent_t<Type>>>::resolve
|
||||
};
|
||||
|
||||
return &node;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename... Type>
|
||||
struct meta_info: meta_node<std::remove_cv_t<std::remove_reference_t<Type>>...> {};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
48
src/entt/meta/pointer.hpp
Normal file
48
src/entt/meta/pointer.hpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#ifndef ENTT_META_POINTER_HPP
|
||||
#define ENTT_META_POINTER_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include "type_traits.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Makes plain pointers pointer-like types for the meta system.
|
||||
* @tparam Type Element type.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct is_meta_pointer_like<Type *>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Makes `std::shared_ptr`s of any type pointer-like types for the meta
|
||||
* system.
|
||||
* @tparam Type Element type.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct is_meta_pointer_like<std::shared_ptr<Type>>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Makes `std::unique_ptr`s of any type pointer-like types for the meta
|
||||
* system.
|
||||
* @tparam Type Element type.
|
||||
* @tparam Args Other arguments.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
struct is_meta_pointer_like<std::unique_ptr<Type, Args...>>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -5,12 +5,12 @@
|
||||
namespace entt {
|
||||
|
||||
|
||||
/*! @brief Empty class type used to request the _as alias_ policy. */
|
||||
struct as_alias_t {};
|
||||
/*! @brief Empty class type used to request the _as ref_ policy. */
|
||||
struct as_ref_t {};
|
||||
|
||||
|
||||
/*! @brief Disambiguation tag. */
|
||||
constexpr as_alias_t as_alias;
|
||||
inline constexpr as_ref_t as_ref;
|
||||
|
||||
|
||||
/*! @brief Empty class type used to request the _as-is_ policy. */
|
||||
@@ -24,4 +24,4 @@ struct as_void_t {};
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_META_POLICY_HPP
|
||||
#endif
|
||||
|
||||
98
src/entt/meta/range.hpp
Normal file
98
src/entt/meta/range.hpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#ifndef ENTT_META_RANGE_HPP
|
||||
#define ENTT_META_RANGE_HPP
|
||||
|
||||
|
||||
#include "internal.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Iterable range to use to iterate all types of meta objects.
|
||||
* @tparam Type Type of meta objects iterated.
|
||||
*/
|
||||
template<typename Type>
|
||||
class meta_range {
|
||||
struct range_iterator {
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = Type;
|
||||
using pointer = void;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using node_type = typename Type::node_type;
|
||||
|
||||
range_iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
range_iterator(node_type *head) ENTT_NOEXCEPT
|
||||
: it{head}
|
||||
{}
|
||||
|
||||
range_iterator & operator++() ENTT_NOEXCEPT {
|
||||
return ++it, *this;
|
||||
}
|
||||
|
||||
range_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
range_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
|
||||
return it.operator->();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const range_iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.it == it;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator!=(const range_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
typename internal::meta_range<node_type>::iterator it{};
|
||||
};
|
||||
|
||||
public:
|
||||
/*! @brief Node type. */
|
||||
using node_type = typename Type::node_type;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator = range_iterator;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
meta_range() ENTT_NOEXCEPT = default;
|
||||
|
||||
/**
|
||||
* @brief Constructs a meta range from a given node.
|
||||
* @param head The underlying node with which to construct the range.
|
||||
*/
|
||||
meta_range(node_type *head)
|
||||
: node{head}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
* @return An iterator to the first meta object of the range.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
|
||||
return iterator{node};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end.
|
||||
* @return An iterator to the element following the last meta object of the
|
||||
* range.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
|
||||
return iterator{};
|
||||
}
|
||||
|
||||
private:
|
||||
node_type *node{nullptr};
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
59
src/entt/meta/resolve.hpp
Normal file
59
src/entt/meta/resolve.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef ENTT_META_RESOLVE_HPP
|
||||
#define ENTT_META_RESOLVE_HPP
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include "ctx.hpp"
|
||||
#include "meta.hpp"
|
||||
#include "range.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns the meta type associated with a given type.
|
||||
* @tparam Type Type to use to search for a meta type.
|
||||
* @return The meta type associated with the given type, if any.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] meta_type resolve() ENTT_NOEXCEPT {
|
||||
return internal::meta_info<Type>::resolve();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns a range to use to visit all meta types.
|
||||
* @return An iterable range to use to visit all meta types.
|
||||
*/
|
||||
[[nodiscard]] inline meta_range<meta_type> resolve() {
|
||||
return *internal::meta_context::global();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns the meta type associated with a given identifier, if any.
|
||||
* @param id Unique identifier.
|
||||
* @return The meta type associated with the given identifier, if any.
|
||||
*/
|
||||
[[nodiscard]] inline meta_type resolve_id(const id_type id) ENTT_NOEXCEPT {
|
||||
internal::meta_range range{*internal::meta_context::global()};
|
||||
return std::find_if(range.begin(), range.end(), [id](const auto &curr) { return curr.id == id; }).operator->();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns the meta type associated with a given type id, if any.
|
||||
* @param id Unique identifier.
|
||||
* @return The meta type associated with the given type id, if any.
|
||||
*/
|
||||
[[nodiscard]] inline meta_type resolve_type(const id_type id) ENTT_NOEXCEPT {
|
||||
internal::meta_range range{*internal::meta_context::global()};
|
||||
return std::find_if(range.begin(), range.end(), [id](const auto &curr) { return curr.type_id == id; }).operator->();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
119
src/entt/meta/type_traits.hpp
Normal file
119
src/entt/meta/type_traits.hpp
Normal file
@@ -0,0 +1,119 @@
|
||||
#ifndef ENTT_META_TYPE_TRAITS_HPP
|
||||
#define ENTT_META_TYPE_TRAITS_HPP
|
||||
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Traits class template to be specialized to enable support for meta
|
||||
* sequence containers.
|
||||
*/
|
||||
template<typename>
|
||||
struct meta_sequence_container_traits;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Traits class template to be specialized to enable support for meta
|
||||
* associative containers.
|
||||
*/
|
||||
template<typename>
|
||||
struct meta_associative_container_traits;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if support for meta
|
||||
* sequence containers is enabled for the given type, false otherwise.
|
||||
* @tparam Type Potentially sequence container type.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
struct has_meta_sequence_container_traits: std::false_type {};
|
||||
|
||||
|
||||
/*! @copydoc has_meta_sequence_container_traits */
|
||||
template<typename Type>
|
||||
struct has_meta_sequence_container_traits<Type, std::void_t<typename meta_sequence_container_traits<Type>::value_type>>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type Potentially sequence container type.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr auto has_meta_sequence_container_traits_v = has_meta_sequence_container_traits<Type>::value;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if support for meta
|
||||
* associative containers is enabled for the given type, false otherwise.
|
||||
* @tparam Type Potentially associative container type.
|
||||
*/
|
||||
template<typename, typename = void>
|
||||
struct has_meta_associative_container_traits: std::false_type {};
|
||||
|
||||
|
||||
/*! @copydoc has_meta_associative_container_traits */
|
||||
template<typename Type>
|
||||
struct has_meta_associative_container_traits<Type, std::void_t<typename meta_associative_container_traits<Type>::key_type>>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type Potentially associative container type.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr auto has_meta_associative_container_traits_v = has_meta_associative_container_traits<Type>::value;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a meta associative
|
||||
* container claims to wrap a key-only type, false otherwise.
|
||||
* @tparam Type Potentially key-only meta associative container type.
|
||||
*/
|
||||
template<typename, typename = void>
|
||||
struct is_key_only_meta_associative_container: std::true_type {};
|
||||
|
||||
|
||||
/*! @copydoc is_key_only_meta_associative_container */
|
||||
template<typename Type>
|
||||
struct is_key_only_meta_associative_container<Type, std::void_t<typename meta_associative_container_traits<Type>::mapped_type>>
|
||||
: std::false_type
|
||||
{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type Potentially key-only meta associative container type.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr auto is_key_only_meta_associative_container_v = is_key_only_meta_associative_container<Type>::value;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type is a
|
||||
* pointer-like type from the point of view of the meta system, false otherwise.
|
||||
* @tparam Type Potentially pointer-like type.
|
||||
*/
|
||||
template<typename>
|
||||
struct is_meta_pointer_like: std::false_type {};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type Potentially pointer-like type.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like<Type>::value;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
86
src/entt/platform/android-ndk-r17.hpp
Normal file
86
src/entt/platform/android-ndk-r17.hpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef ENTT_PLATFORM_ANDROID_NDK_R17_HPP
|
||||
#define ENTT_PLATFORM_ANDROID_NDK_R17_HPP
|
||||
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <android/ndk-version.h>
|
||||
#if __NDK_MAJOR__ == 17
|
||||
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
|
||||
namespace std {
|
||||
|
||||
|
||||
namespace internal {
|
||||
|
||||
|
||||
template<typename Func, typename... Args>
|
||||
constexpr auto is_invocable(int) -> decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...), std::true_type{});
|
||||
|
||||
|
||||
template<typename, typename...>
|
||||
constexpr std::false_type is_invocable(...);
|
||||
|
||||
|
||||
template<typename Ret, typename Func, typename... Args>
|
||||
constexpr auto is_invocable_r(int)
|
||||
-> std::enable_if_t<decltype(std::is_convertible_v<decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...)), Ret>, std::true_type>;
|
||||
|
||||
|
||||
template<typename, typename, typename...>
|
||||
constexpr std::false_type is_invocable_r(...);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
template<typename Func, typename... Args>
|
||||
struct is_invocable: decltype(internal::is_invocable<Func, Args...>(0)) {};
|
||||
|
||||
|
||||
template<typename Func, typename... Argsv>
|
||||
inline constexpr bool is_invocable_v = std::is_invocable<Func, Args...>::value;
|
||||
|
||||
|
||||
template<typename Ret, typename Func, typename... Args>
|
||||
struct is_invocable_r: decltype(internal::is_invocable_r<Ret, Func, Args...>(0)) {};
|
||||
|
||||
|
||||
template<typename Ret, typename Func, typename... Args>
|
||||
inline constexpr bool is_invocable_r_v = std::is_invocable_r<Ret, Func, Args...>::value;
|
||||
|
||||
|
||||
template<typename Func, typename...Args>
|
||||
struct invoke_result {
|
||||
using type = decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...));
|
||||
};
|
||||
|
||||
|
||||
template<typename Func, typename... Args>
|
||||
using invoke_result_t = typename std::invoke_result<Func, Args...>::type;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
|
||||
#endif
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "../core/type_traits.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
@@ -81,41 +82,37 @@ class process {
|
||||
FINISHED
|
||||
};
|
||||
|
||||
template<state value>
|
||||
using state_value_t = std::integral_constant<state, value>;
|
||||
|
||||
template<typename Target = Derived>
|
||||
auto tick(int, state_value_t<state::UNINITIALIZED>)
|
||||
-> decltype(std::declval<Target>().init()) {
|
||||
auto next(integral_constant<state::UNINITIALIZED>)
|
||||
-> decltype(std::declval<Target>().init(), void()) {
|
||||
static_cast<Target *>(this)->init();
|
||||
}
|
||||
|
||||
template<typename Target = Derived>
|
||||
auto tick(int, state_value_t<state::RUNNING>, Delta delta, void *data)
|
||||
-> decltype(std::declval<Target>().update(delta, data)) {
|
||||
auto next(integral_constant<state::RUNNING>, Delta delta, void *data)
|
||||
-> decltype(std::declval<Target>().update(delta, data), void()) {
|
||||
static_cast<Target *>(this)->update(delta, data);
|
||||
}
|
||||
|
||||
template<typename Target = Derived>
|
||||
auto tick(int, state_value_t<state::SUCCEEDED>)
|
||||
-> decltype(std::declval<Target>().succeeded()) {
|
||||
auto next(integral_constant<state::SUCCEEDED>)
|
||||
-> decltype(std::declval<Target>().succeeded(), void()) {
|
||||
static_cast<Target *>(this)->succeeded();
|
||||
}
|
||||
|
||||
template<typename Target = Derived>
|
||||
auto tick(int, state_value_t<state::FAILED>)
|
||||
-> decltype(std::declval<Target>().failed()) {
|
||||
auto next(integral_constant<state::FAILED>)
|
||||
-> decltype(std::declval<Target>().failed(), void()) {
|
||||
static_cast<Target *>(this)->failed();
|
||||
}
|
||||
|
||||
template<typename Target = Derived>
|
||||
auto tick(int, state_value_t<state::ABORTED>)
|
||||
-> decltype(std::declval<Target>().aborted()) {
|
||||
auto next(integral_constant<state::ABORTED>)
|
||||
-> decltype(std::declval<Target>().aborted(), void()) {
|
||||
static_cast<Target *>(this)->aborted();
|
||||
}
|
||||
|
||||
template<state value, typename... Args>
|
||||
void tick(char, state_value_t<value>, Args &&...) const ENTT_NOEXCEPT {}
|
||||
void next(...) const ENTT_NOEXCEPT {}
|
||||
|
||||
protected:
|
||||
/**
|
||||
@@ -171,8 +168,8 @@ public:
|
||||
using delta_type = Delta;
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
virtual ~process() ENTT_NOEXCEPT {
|
||||
static_assert(std::is_base_of_v<process, Derived>);
|
||||
virtual ~process() {
|
||||
static_assert(std::is_base_of_v<process, Derived>, "Incorrect use of the class template");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,12 +180,12 @@ public:
|
||||
*
|
||||
* @param immediately Requests an immediate operation.
|
||||
*/
|
||||
void abort(const bool immediately = false) ENTT_NOEXCEPT {
|
||||
void abort(const bool immediately = false) {
|
||||
if(alive()) {
|
||||
current = state::ABORTED;
|
||||
|
||||
if(immediately) {
|
||||
tick(0);
|
||||
tick({});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,7 +194,7 @@ public:
|
||||
* @brief Returns true if a process is either running or paused.
|
||||
* @return True if the process is still alive, false otherwise.
|
||||
*/
|
||||
bool alive() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool alive() const ENTT_NOEXCEPT {
|
||||
return current == state::RUNNING || current == state::PAUSED;
|
||||
}
|
||||
|
||||
@@ -205,7 +202,7 @@ public:
|
||||
* @brief Returns true if a process is already terminated.
|
||||
* @return True if the process is terminated, false otherwise.
|
||||
*/
|
||||
bool dead() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool dead() const ENTT_NOEXCEPT {
|
||||
return current == state::FINISHED;
|
||||
}
|
||||
|
||||
@@ -213,7 +210,7 @@ public:
|
||||
* @brief Returns true if a process is currently paused.
|
||||
* @return True if the process is paused, false otherwise.
|
||||
*/
|
||||
bool paused() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool paused() const ENTT_NOEXCEPT {
|
||||
return current == state::PAUSED;
|
||||
}
|
||||
|
||||
@@ -221,7 +218,7 @@ public:
|
||||
* @brief Returns true if a process terminated with errors.
|
||||
* @return True if the process terminated with errors, false otherwise.
|
||||
*/
|
||||
bool rejected() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool rejected() const ENTT_NOEXCEPT {
|
||||
return stopped;
|
||||
}
|
||||
|
||||
@@ -233,11 +230,11 @@ public:
|
||||
void tick(const Delta delta, void *data = nullptr) {
|
||||
switch (current) {
|
||||
case state::UNINITIALIZED:
|
||||
tick(0, state_value_t<state::UNINITIALIZED>{});
|
||||
next(integral_constant<state::UNINITIALIZED>{});
|
||||
current = state::RUNNING;
|
||||
break;
|
||||
case state::RUNNING:
|
||||
tick(0, state_value_t<state::RUNNING>{}, delta, data);
|
||||
next(integral_constant<state::RUNNING>{}, delta, data);
|
||||
break;
|
||||
default:
|
||||
// suppress warnings
|
||||
@@ -247,16 +244,16 @@ public:
|
||||
// if it's dead, it must be notified and removed immediately
|
||||
switch(current) {
|
||||
case state::SUCCEEDED:
|
||||
tick(0, state_value_t<state::SUCCEEDED>{});
|
||||
next(integral_constant<state::SUCCEEDED>{});
|
||||
current = state::FINISHED;
|
||||
break;
|
||||
case state::FAILED:
|
||||
tick(0, state_value_t<state::FAILED>{});
|
||||
next(integral_constant<state::FAILED>{});
|
||||
current = state::FINISHED;
|
||||
stopped = true;
|
||||
break;
|
||||
case state::ABORTED:
|
||||
tick(0, state_value_t<state::ABORTED>{});
|
||||
next(integral_constant<state::ABORTED>{});
|
||||
current = state::FINISHED;
|
||||
stopped = true;
|
||||
break;
|
||||
@@ -337,4 +334,4 @@ struct process_adaptor: process<process_adaptor<Func, Delta>, Delta>, private Fu
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_PROCESS_PROCESS_HPP
|
||||
#endif
|
||||
|
||||
@@ -63,7 +63,7 @@ class scheduler {
|
||||
|
||||
template<typename Proc, typename... Args>
|
||||
continuation then(Args &&... args) {
|
||||
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>);
|
||||
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
|
||||
auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &scheduler::deleter<Proc>};
|
||||
handler->next.reset(new process_handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr});
|
||||
handler = handler->next.get();
|
||||
@@ -80,7 +80,7 @@ class scheduler {
|
||||
};
|
||||
|
||||
template<typename Proc>
|
||||
static bool update(process_handler &handler, const Delta delta, void *data) {
|
||||
[[nodiscard]] static bool update(process_handler &handler, const Delta delta, void *data) {
|
||||
auto *process = static_cast<Proc *>(handler.instance.get());
|
||||
process->tick(delta, data);
|
||||
|
||||
@@ -111,10 +111,10 @@ class scheduler {
|
||||
|
||||
public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename std::vector<process_handler>::size_type;
|
||||
using size_type = std::size_t;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
scheduler() ENTT_NOEXCEPT = default;
|
||||
scheduler() = default;
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
scheduler(scheduler &&) = default;
|
||||
@@ -126,7 +126,7 @@ public:
|
||||
* @brief Number of processes currently scheduled.
|
||||
* @return Number of processes currently scheduled.
|
||||
*/
|
||||
size_type size() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
|
||||
return handlers.size();
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ public:
|
||||
* @brief Returns true if at least a process is currently scheduled.
|
||||
* @return True if there are scheduled processes, false otherwise.
|
||||
*/
|
||||
bool empty() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return handlers.empty();
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ public:
|
||||
*/
|
||||
template<typename Proc, typename... Args>
|
||||
auto attach(Args &&... args) {
|
||||
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>);
|
||||
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
|
||||
auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &scheduler::deleter<Proc>};
|
||||
process_handler handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr};
|
||||
// forces the process to exit the uninitialized state
|
||||
@@ -281,9 +281,9 @@ public:
|
||||
decltype(handlers) exec;
|
||||
exec.swap(handlers);
|
||||
|
||||
std::for_each(exec.begin(), exec.end(), [immediately](auto &handler) {
|
||||
for(auto &&handler: exec) {
|
||||
handler.abort(handler, immediately);
|
||||
});
|
||||
}
|
||||
|
||||
std::move(handlers.begin(), handlers.end(), std::back_inserter(exec));
|
||||
handlers.swap(exec);
|
||||
@@ -297,4 +297,4 @@ private:
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_PROCESS_SCHEDULER_HPP
|
||||
#endif
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "handle.hpp"
|
||||
#include "loader.hpp"
|
||||
#include "fwd.hpp"
|
||||
@@ -26,14 +27,11 @@ namespace entt {
|
||||
* @tparam Resource Type of resources managed by a cache.
|
||||
*/
|
||||
template<typename Resource>
|
||||
class resource_cache {
|
||||
using container_type = std::unordered_map<ENTT_ID_TYPE, std::shared_ptr<Resource>>;
|
||||
|
||||
public:
|
||||
struct resource_cache {
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename container_type::size_type;
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Type of resources managed by a cache. */
|
||||
using resource_type = ENTT_ID_TYPE;
|
||||
using resource_type = Resource;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
resource_cache() = default;
|
||||
@@ -48,7 +46,7 @@ public:
|
||||
* @brief Number of resources managed by a cache.
|
||||
* @return Number of resources currently stored.
|
||||
*/
|
||||
size_type size() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
|
||||
return resources.size();
|
||||
}
|
||||
|
||||
@@ -56,7 +54,7 @@ public:
|
||||
* @brief Returns true if a cache contains no resources, false otherwise.
|
||||
* @return True if the cache contains no resources, false otherwise.
|
||||
*/
|
||||
bool empty() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return resources.empty();
|
||||
}
|
||||
|
||||
@@ -93,20 +91,20 @@ public:
|
||||
* @return A handle for the given resource.
|
||||
*/
|
||||
template<typename Loader, typename... Args>
|
||||
resource_handle<Resource> load(const resource_type id, Args &&... args) {
|
||||
static_assert(std::is_base_of_v<resource_loader<Loader, Resource>, Loader>);
|
||||
resource_handle<Resource> handle{};
|
||||
resource_handle<Resource> load(const id_type id, Args &&... args) {
|
||||
static_assert(std::is_base_of_v<resource_loader<Loader, Resource>, Loader>, "Invalid loader type");
|
||||
resource_handle<Resource> resource{};
|
||||
|
||||
if(auto it = resources.find(id); it == resources.cend()) {
|
||||
if(auto resource = Loader{}.get(std::forward<Args>(args)...); resource) {
|
||||
resources[id] = resource;
|
||||
handle = std::move(resource);
|
||||
if(auto instance = Loader{}.get(std::forward<Args>(args)...); instance) {
|
||||
resources[id] = instance;
|
||||
resource = std::move(instance);
|
||||
}
|
||||
} else {
|
||||
handle = it->second;
|
||||
resource = it->second;
|
||||
}
|
||||
|
||||
return handle;
|
||||
return resource;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,7 +131,7 @@ public:
|
||||
* @return A handle for the given resource.
|
||||
*/
|
||||
template<typename Loader, typename... Args>
|
||||
resource_handle<Resource> reload(const resource_type id, Args &&... args) {
|
||||
resource_handle<Resource> reload(const id_type id, Args &&... args) {
|
||||
return (discard(id), load<Loader>(id, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
@@ -150,7 +148,7 @@ public:
|
||||
* @return A handle for the given resource.
|
||||
*/
|
||||
template<typename Loader, typename... Args>
|
||||
resource_handle<Resource> temp(Args &&... args) const {
|
||||
[[nodiscard]] resource_handle<Resource> temp(Args &&... args) const {
|
||||
return { Loader{}.get(std::forward<Args>(args)...) };
|
||||
}
|
||||
|
||||
@@ -167,7 +165,7 @@ public:
|
||||
* @param id Unique resource identifier.
|
||||
* @return A handle for the given resource.
|
||||
*/
|
||||
resource_handle<Resource> handle(const resource_type id) const {
|
||||
[[nodiscard]] resource_handle<Resource> handle(const id_type id) const {
|
||||
auto it = resources.find(id);
|
||||
return { it == resources.end() ? nullptr : it->second };
|
||||
}
|
||||
@@ -177,7 +175,7 @@ public:
|
||||
* @param id Unique resource identifier.
|
||||
* @return True if the cache contains the resource, false otherwise.
|
||||
*/
|
||||
bool contains(const resource_type id) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool contains(const id_type id) const {
|
||||
return (resources.find(id) != resources.cend());
|
||||
}
|
||||
|
||||
@@ -189,7 +187,7 @@ public:
|
||||
*
|
||||
* @param id Unique resource identifier.
|
||||
*/
|
||||
void discard(const resource_type id) ENTT_NOEXCEPT {
|
||||
void discard(const id_type id) {
|
||||
if(auto it = resources.find(id); it != resources.end()) {
|
||||
resources.erase(it);
|
||||
}
|
||||
@@ -204,9 +202,9 @@ public:
|
||||
* forms:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const resource_type);
|
||||
* void(resource_handle<Resource>);
|
||||
* void(const resource_type, resource_handle<Resource>);
|
||||
* void(const entt::id_type);
|
||||
* void(entt::resource_handle<Resource>);
|
||||
* void(const entt::id_type, entt::resource_handle<Resource>);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
@@ -220,7 +218,7 @@ public:
|
||||
while(begin != end) {
|
||||
auto curr = begin++;
|
||||
|
||||
if constexpr(std::is_invocable_v<Func, resource_type>) {
|
||||
if constexpr(std::is_invocable_v<Func, id_type>) {
|
||||
func(curr->first);
|
||||
} else if constexpr(std::is_invocable_v<Func, resource_handle<Resource>>) {
|
||||
func(resource_handle{ curr->second });
|
||||
@@ -231,11 +229,11 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
container_type resources;
|
||||
std::unordered_map<id_type, std::shared_ptr<Resource>> resources;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_RESOURCE_CACHE_HPP
|
||||
#endif
|
||||
|
||||
@@ -2,21 +2,17 @@
|
||||
#define ENTT_RESOURCE_FWD_HPP
|
||||
|
||||
|
||||
#include "../config/config.h"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/*! @class resource_cache */
|
||||
template<typename>
|
||||
class resource_cache;
|
||||
struct resource_cache;
|
||||
|
||||
|
||||
/*! @class resource_handle */
|
||||
template<typename>
|
||||
class resource_handle;
|
||||
|
||||
/*! @class resource_loader */
|
||||
|
||||
template<typename, typename>
|
||||
class resource_loader;
|
||||
|
||||
@@ -24,4 +20,4 @@ class resource_loader;
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_RESOURCE_FWD_HPP
|
||||
#endif
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace entt {
|
||||
template<typename Resource>
|
||||
class resource_handle {
|
||||
/*! @brief Resource handles are friends of their caches. */
|
||||
friend class resource_cache<Resource>;
|
||||
friend struct resource_cache<Resource>;
|
||||
|
||||
resource_handle(std::shared_ptr<Resource> res) ENTT_NOEXCEPT
|
||||
: resource{std::move(res)}
|
||||
@@ -46,27 +46,35 @@ public:
|
||||
*
|
||||
* @return A reference to the managed resource.
|
||||
*/
|
||||
const Resource & get() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] const Resource & get() const ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(static_cast<bool>(resource));
|
||||
return *resource;
|
||||
}
|
||||
|
||||
/*! @copydoc get */
|
||||
Resource & get() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] Resource & get() ENTT_NOEXCEPT {
|
||||
return const_cast<Resource &>(std::as_const(*this).get());
|
||||
}
|
||||
|
||||
/*! @copydoc get */
|
||||
operator const Resource & () const ENTT_NOEXCEPT { return get(); }
|
||||
[[nodiscard]] operator const Resource & () const ENTT_NOEXCEPT {
|
||||
return get();
|
||||
}
|
||||
|
||||
/*! @copydoc get */
|
||||
operator Resource & () ENTT_NOEXCEPT { return get(); }
|
||||
[[nodiscard]] operator Resource & () ENTT_NOEXCEPT {
|
||||
return get();
|
||||
}
|
||||
|
||||
/*! @copydoc get */
|
||||
const Resource & operator *() const ENTT_NOEXCEPT { return get(); }
|
||||
[[nodiscard]] const Resource & operator *() const ENTT_NOEXCEPT {
|
||||
return get();
|
||||
}
|
||||
|
||||
/*! @copydoc get */
|
||||
Resource & operator *() ENTT_NOEXCEPT { return get(); }
|
||||
[[nodiscard]] Resource & operator *() ENTT_NOEXCEPT {
|
||||
return get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a pointer to the managed resource.
|
||||
@@ -79,13 +87,13 @@ public:
|
||||
* @return A pointer to the managed resource or `nullptr` if the handle
|
||||
* contains no resource at all.
|
||||
*/
|
||||
const Resource * operator->() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] const Resource * operator->() const ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(static_cast<bool>(resource));
|
||||
return resource.get();
|
||||
}
|
||||
|
||||
/*! @copydoc operator-> */
|
||||
Resource * operator->() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] Resource * operator->() ENTT_NOEXCEPT {
|
||||
return const_cast<Resource *>(std::as_const(*this).operator->());
|
||||
}
|
||||
|
||||
@@ -93,7 +101,9 @@ public:
|
||||
* @brief Returns true if a handle contains a resource, false otherwise.
|
||||
* @return True if the handle contains a resource, false otherwise.
|
||||
*/
|
||||
explicit operator bool() const { return static_cast<bool>(resource); }
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return static_cast<bool>(resource);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Resource> resource;
|
||||
@@ -103,4 +113,4 @@ private:
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_RESOURCE_HANDLE_HPP
|
||||
#endif
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace entt {
|
||||
template<typename Loader, typename Resource>
|
||||
class resource_loader {
|
||||
/*! @brief Resource loaders are friends of their caches. */
|
||||
friend class resource_cache<Resource>;
|
||||
friend struct resource_cache<Resource>;
|
||||
|
||||
/**
|
||||
* @brief Loads the resource and returns it.
|
||||
@@ -53,7 +53,7 @@ class resource_loader {
|
||||
* @return The resource just loaded or an empty pointer in case of errors.
|
||||
*/
|
||||
template<typename... Args>
|
||||
std::shared_ptr<Resource> get(Args &&... args) const {
|
||||
[[nodiscard]] std::shared_ptr<Resource> get(Args &&... args) const {
|
||||
return static_cast<const Loader *>(this)->load(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
@@ -62,4 +62,4 @@ class resource_loader {
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_RESOURCE_LOADER_HPP
|
||||
#endif
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
|
||||
|
||||
#include <tuple>
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
@@ -24,37 +23,33 @@ namespace internal {
|
||||
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
auto to_function_pointer(Ret(*)(Args...)) -> Ret(*)(Args...);
|
||||
auto function_pointer(Ret(*)(Args...)) -> Ret(*)(Args...);
|
||||
|
||||
|
||||
template<typename Ret, typename... Args, typename Type, typename Payload, typename = std::enable_if_t<std::is_convertible_v<Payload &, Type &>>>
|
||||
auto to_function_pointer(Ret(*)(Type &, Args...), Payload &) -> Ret(*)(Args...);
|
||||
template<typename Ret, typename Type, typename... Args, typename Other>
|
||||
auto function_pointer(Ret(*)(Type, Args...), Other &&) -> Ret(*)(Args...);
|
||||
|
||||
|
||||
template<typename Class, typename Ret, typename... Args>
|
||||
auto to_function_pointer(Ret(Class:: *)(Args...), const Class &) -> Ret(*)(Args...);
|
||||
template<typename Class, typename Ret, typename... Args, typename... Other>
|
||||
auto function_pointer(Ret(Class:: *)(Args...), Other &&...) -> Ret(*)(Args...);
|
||||
|
||||
|
||||
template<typename Class, typename Ret, typename... Args>
|
||||
auto to_function_pointer(Ret(Class:: *)(Args...) const, const Class &) -> Ret(*)(Args...);
|
||||
template<typename Class, typename Ret, typename... Args, typename... Other>
|
||||
auto function_pointer(Ret(Class:: *)(Args...) const, Other &&...) -> Ret(*)(Args...);
|
||||
|
||||
|
||||
template<typename Class, typename Type>
|
||||
auto to_function_pointer(Type Class:: *, const Class &) -> Type(*)();
|
||||
template<typename Class, typename Type, typename... Other>
|
||||
auto function_pointer(Type Class:: *, Other &&...) -> Type(*)();
|
||||
|
||||
|
||||
template<typename>
|
||||
struct function_extent;
|
||||
template<typename... Type>
|
||||
using function_pointer_t = decltype(internal::function_pointer(std::declval<Type>()...));
|
||||
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
struct function_extent<Ret(*)(Args...)> {
|
||||
static constexpr auto value = sizeof...(Args);
|
||||
};
|
||||
|
||||
|
||||
template<typename Func>
|
||||
constexpr auto function_extent_v = function_extent<Func>::value;
|
||||
template<typename... Class, typename Ret, typename... Args>
|
||||
[[nodiscard]] constexpr auto index_sequence_for(Ret(*)(Args...)) {
|
||||
return std::index_sequence_for<Class..., Args...>{};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -62,7 +57,7 @@ constexpr auto function_extent_v = function_extent<Func>::value;
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond TURN_OFF_DOXYGEN
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
|
||||
@@ -73,7 +68,7 @@ struct connect_arg_t {};
|
||||
|
||||
/*! @brief Constant of type connect_arg_t used to disambiguate calls. */
|
||||
template<auto Func>
|
||||
constexpr connect_arg_t<Func> connect_arg{};
|
||||
inline constexpr connect_arg_t<Func> connect_arg{};
|
||||
|
||||
|
||||
/**
|
||||
@@ -92,50 +87,47 @@ class delegate;
|
||||
* Unmanaged delegate for function pointers and members. Users of this class are
|
||||
* in charge of disconnecting instances before deleting them.
|
||||
*
|
||||
* A delegate can be used as general purpose invoker with no memory overhead for
|
||||
* free functions (with or without payload) and members provided along with an
|
||||
* instance on which to invoke them.
|
||||
* A delegate can be used as a general purpose invoker without memory overhead
|
||||
* for free functions possibly with payloads and bound or unbound members.
|
||||
*
|
||||
* @tparam Ret Return type of a function type.
|
||||
* @tparam Args Types of arguments of a function type.
|
||||
*/
|
||||
template<typename Ret, typename... Args>
|
||||
class delegate<Ret(Args...)> {
|
||||
using proto_fn_type = Ret(const void *, std::tuple<Args &&...>);
|
||||
|
||||
template<auto Function, std::size_t... Index>
|
||||
void connect(std::index_sequence<Index...>) ENTT_NOEXCEPT {
|
||||
static_assert(std::is_invocable_r_v<Ret, decltype(Function), std::tuple_element_t<Index, std::tuple<Args...>>...>);
|
||||
data = nullptr;
|
||||
|
||||
fn = [](const void *, std::tuple<Args &&...> args) -> Ret {
|
||||
// Ret(...) makes void(...) eat the return values to avoid errors
|
||||
return Ret(std::invoke(Function, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(args))...));
|
||||
template<auto Candidate, std::size_t... Index>
|
||||
[[nodiscard]] auto wrap(std::index_sequence<Index...>) ENTT_NOEXCEPT {
|
||||
return [](const void *, Args... args) -> Ret {
|
||||
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
|
||||
return Ret(std::invoke(Candidate, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(arguments))...));
|
||||
};
|
||||
}
|
||||
|
||||
template<auto Candidate, typename Type, std::size_t... Index>
|
||||
void connect(Type &value_or_instance, std::index_sequence<Index...>) ENTT_NOEXCEPT {
|
||||
static_assert(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, std::tuple_element_t<Index, std::tuple<Args...>>...>);
|
||||
data = &value_or_instance;
|
||||
[[nodiscard]] auto wrap(Type &, std::index_sequence<Index...>) ENTT_NOEXCEPT {
|
||||
return [](const void *payload, Args... args) -> Ret {
|
||||
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
|
||||
Type *curr = static_cast<Type *>(const_cast<std::conditional_t<std::is_const_v<Type>, const void *, void *>>(payload));
|
||||
return Ret(std::invoke(Candidate, *curr, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(arguments))...));
|
||||
};
|
||||
}
|
||||
|
||||
fn = [](const void *payload, std::tuple<Args &&...> args) -> Ret {
|
||||
Type *curr = nullptr;
|
||||
|
||||
if constexpr(std::is_const_v<Type>) {
|
||||
curr = static_cast<Type *>(payload);
|
||||
} else {
|
||||
curr = static_cast<Type *>(const_cast<void *>(payload));
|
||||
}
|
||||
|
||||
// Ret(...) makes void(...) eat the return values to avoid errors
|
||||
return Ret(std::invoke(Candidate, *curr, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(args))...));
|
||||
template<auto Candidate, typename Type, std::size_t... Index>
|
||||
[[nodiscard]] auto wrap(Type *, std::index_sequence<Index...>) ENTT_NOEXCEPT {
|
||||
return [](const void *payload, Args... args) -> Ret {
|
||||
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
|
||||
Type *curr = static_cast<Type *>(const_cast<std::conditional_t<std::is_const_v<Type>, const void *, void *>>(payload));
|
||||
return Ret(std::invoke(Candidate, curr, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(arguments))...));
|
||||
};
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Function type of the contained target. */
|
||||
using function_type = Ret(const void *, Args...);
|
||||
/*! @brief Function type of the delegate. */
|
||||
using function_type = Ret(Args...);
|
||||
using type = Ret(Args...);
|
||||
/*! @brief Return type of the delegate. */
|
||||
using result_type = Ret;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
delegate() ENTT_NOEXCEPT
|
||||
@@ -143,59 +135,125 @@ public:
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Constructs a delegate and connects a free function to it.
|
||||
* @tparam Function A valid free function pointer.
|
||||
* @brief Constructs a delegate and connects a free function or an unbound
|
||||
* member.
|
||||
* @tparam Candidate Function or member to connect to the delegate.
|
||||
*/
|
||||
template<auto Function>
|
||||
delegate(connect_arg_t<Function>) ENTT_NOEXCEPT
|
||||
: delegate{}
|
||||
{
|
||||
connect<Function>();
|
||||
template<auto Candidate>
|
||||
delegate(connect_arg_t<Candidate>) ENTT_NOEXCEPT {
|
||||
connect<Candidate>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs a delegate and connects a member for a given instance
|
||||
* or a free function with payload.
|
||||
* @tparam Candidate Member or free function to connect to the delegate.
|
||||
* @brief Constructs a delegate and connects a free function with payload or
|
||||
* a bound member.
|
||||
* @tparam Candidate Function or member to connect to the delegate.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid reference that fits the purpose.
|
||||
* @param value_or_instance A valid object that fits the purpose.
|
||||
*/
|
||||
template<auto Candidate, typename Type>
|
||||
delegate(connect_arg_t<Candidate>, Type &value_or_instance) ENTT_NOEXCEPT
|
||||
: delegate{}
|
||||
{
|
||||
connect<Candidate>(value_or_instance);
|
||||
delegate(connect_arg_t<Candidate>, Type &&value_or_instance) ENTT_NOEXCEPT {
|
||||
connect<Candidate>(std::forward<Type>(value_or_instance));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects a free function to a delegate.
|
||||
* @tparam Function A valid free function pointer.
|
||||
* @brief Constructs a delegate and connects an user defined function with
|
||||
* optional payload.
|
||||
* @param function Function to connect to the delegate.
|
||||
* @param payload User defined arbitrary data.
|
||||
*/
|
||||
template<auto Function>
|
||||
void connect() ENTT_NOEXCEPT {
|
||||
constexpr auto extent = internal::function_extent_v<decltype(internal::to_function_pointer(std::declval<decltype(Function)>()))>;
|
||||
connect<Function>(std::make_index_sequence<extent>{});
|
||||
delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT {
|
||||
connect(function, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects a member function for a given instance or a free function
|
||||
* with payload to a delegate.
|
||||
* @brief Connects a free function or an unbound member to a delegate.
|
||||
* @tparam Candidate Function or member to connect to the delegate.
|
||||
*/
|
||||
template<auto Candidate>
|
||||
void connect() ENTT_NOEXCEPT {
|
||||
data = nullptr;
|
||||
|
||||
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
|
||||
fn = [](const void *, Args... args) -> Ret {
|
||||
return Ret(std::invoke(Candidate, std::forward<Args>(args)...));
|
||||
};
|
||||
} else if constexpr(std::is_member_pointer_v<decltype(Candidate)>) {
|
||||
fn = wrap<Candidate>(internal::index_sequence_for<std::tuple_element_t<0, std::tuple<Args...>>>(internal::function_pointer_t<decltype(Candidate)>{}));
|
||||
} else {
|
||||
fn = wrap<Candidate>(internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate)>{}));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects a free function with payload or a bound member to a
|
||||
* delegate.
|
||||
*
|
||||
* The delegate isn't responsible for the connected object or the payload.
|
||||
* Users must always guarantee that the lifetime of the instance overcomes
|
||||
* the one of the delegate.<br/>
|
||||
* the one of the delegate.<br/>
|
||||
* When used to connect a free function with payload, its signature must be
|
||||
* such that the instance is the first argument before the ones used to
|
||||
* define the delegate itself.
|
||||
*
|
||||
* @tparam Candidate Member or free function to connect to the delegate.
|
||||
* @tparam Candidate Function or member to connect to the delegate.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid reference that fits the purpose.
|
||||
*/
|
||||
template<auto Candidate, typename Type>
|
||||
void connect(Type &value_or_instance) ENTT_NOEXCEPT {
|
||||
constexpr auto extent = internal::function_extent_v<decltype(internal::to_function_pointer(std::declval<decltype(Candidate)>(), value_or_instance))>;
|
||||
connect<Candidate>(value_or_instance, std::make_index_sequence<extent>{});
|
||||
data = &value_or_instance;
|
||||
|
||||
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, Args...>) {
|
||||
fn = [](const void *payload, Args... args) -> Ret {
|
||||
Type *curr = static_cast<Type *>(const_cast<std::conditional_t<std::is_const_v<Type>, const void *, void *>>(payload));
|
||||
return Ret(std::invoke(Candidate, *curr, std::forward<Args>(args)...));
|
||||
};
|
||||
} else {
|
||||
fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{}));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects a free function with payload or a bound member to a
|
||||
* delegate.
|
||||
*
|
||||
* @sa connect(Type &)
|
||||
*
|
||||
* @tparam Candidate Function or member to connect to the delegate.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid pointer that fits the purpose.
|
||||
*/
|
||||
template<auto Candidate, typename Type>
|
||||
void connect(Type *value_or_instance) ENTT_NOEXCEPT {
|
||||
data = value_or_instance;
|
||||
|
||||
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, Args...>) {
|
||||
fn = [](const void *payload, Args... args) -> Ret {
|
||||
Type *curr = static_cast<Type *>(const_cast<std::conditional_t<std::is_const_v<Type>, const void *, void *>>(payload));
|
||||
return Ret(std::invoke(Candidate, curr, std::forward<Args>(args)...));
|
||||
};
|
||||
} else {
|
||||
fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{}));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects an user defined function with optional payload to a
|
||||
* delegate.
|
||||
*
|
||||
* The delegate isn't responsible for the connected object or the payload.
|
||||
* Users must always guarantee that the lifetime of an instance overcomes
|
||||
* the one of the delegate.<br/>
|
||||
* The payload is returned as the first argument to the target function in
|
||||
* all cases.
|
||||
*
|
||||
* @param function Function to connect to the delegate.
|
||||
* @param payload User defined arbitrary data.
|
||||
*/
|
||||
void connect(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT {
|
||||
fn = function;
|
||||
data = payload;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -212,7 +270,7 @@ public:
|
||||
* @brief Returns the instance or the payload linked to a delegate, if any.
|
||||
* @return An opaque pointer to the underlying data.
|
||||
*/
|
||||
const void * instance() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] const void * instance() const ENTT_NOEXCEPT {
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -232,16 +290,16 @@ public:
|
||||
*/
|
||||
Ret operator()(Args... args) const {
|
||||
ENTT_ASSERT(fn);
|
||||
return fn(data, std::forward_as_tuple(std::forward<Args>(args)...));
|
||||
return fn(data, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a delegate actually stores a listener.
|
||||
* @return False if the delegate is empty, true otherwise.
|
||||
*/
|
||||
explicit operator bool() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
// no need to test also data
|
||||
return fn;
|
||||
return !(fn == nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,12 +307,12 @@ public:
|
||||
* @param other Delegate with which to compare.
|
||||
* @return False if the two contents differ, true otherwise.
|
||||
*/
|
||||
bool operator==(const delegate<Ret(Args...)> &other) const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool operator==(const delegate<Ret(Args...)> &other) const ENTT_NOEXCEPT {
|
||||
return fn == other.fn && data == other.data;
|
||||
}
|
||||
|
||||
private:
|
||||
proto_fn_type *fn;
|
||||
function_type *fn;
|
||||
const void *data;
|
||||
};
|
||||
|
||||
@@ -268,40 +326,37 @@ private:
|
||||
* @return True if the two contents differ, false otherwise.
|
||||
*/
|
||||
template<typename Ret, typename... Args>
|
||||
bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)> &rhs) ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
*
|
||||
* It allows to deduce the function type of the delegate directly from a
|
||||
* function provided to the constructor.
|
||||
*
|
||||
* @tparam Function A valid free function pointer.
|
||||
* @tparam Candidate Function or member to connect to the delegate.
|
||||
*/
|
||||
template<auto Function>
|
||||
delegate(connect_arg_t<Function>) ENTT_NOEXCEPT
|
||||
-> delegate<std::remove_pointer_t<decltype(internal::to_function_pointer(Function))>>;
|
||||
template<auto Candidate>
|
||||
delegate(connect_arg_t<Candidate>) ENTT_NOEXCEPT
|
||||
-> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate)>>>;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
*
|
||||
* It allows to deduce the function type of the delegate directly from a member
|
||||
* or a free function with payload provided to the constructor.
|
||||
*
|
||||
* @param value_or_instance A valid reference that fits the purpose.
|
||||
* @tparam Candidate Member or free function to connect to the delegate.
|
||||
* @tparam Candidate Function or member to connect to the delegate.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
*/
|
||||
template<auto Candidate, typename Type>
|
||||
delegate(connect_arg_t<Candidate>, Type &value_or_instance) ENTT_NOEXCEPT
|
||||
-> delegate<std::remove_pointer_t<decltype(internal::to_function_pointer(Candidate, value_or_instance))>>;
|
||||
delegate(connect_arg_t<Candidate>, Type &&) ENTT_NOEXCEPT
|
||||
-> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate), Type>>>;
|
||||
|
||||
|
||||
/*! @brief Deduction guide. */
|
||||
template<typename Ret, typename... Args>
|
||||
delegate(Ret(*)(const void *, Args...), const void * = nullptr) ENTT_NOEXCEPT
|
||||
-> delegate<Ret(Args...)>;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_SIGNAL_DELEGATE_HPP
|
||||
#endif
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
#define ENTT_SIGNAL_DISPATCHER_HPP
|
||||
|
||||
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/family.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
#include "sigh.hpp"
|
||||
|
||||
|
||||
@@ -22,117 +23,102 @@ namespace entt {
|
||||
* events to be published all together once per tick.<br/>
|
||||
* Listeners are provided in the form of member functions. For each event of
|
||||
* type `Event`, listeners are such that they can be invoked with an argument of
|
||||
* type `const Event &`, no matter what the return type is.
|
||||
* type `Event &`, no matter what the return type is.
|
||||
*
|
||||
* The types of the instances are `Class &`. Users must guarantee that the
|
||||
* lifetimes of the objects overcome the one of the dispatcher itself to avoid
|
||||
* crashes.
|
||||
* The dispatcher creates instances of the `sigh` class internally. Refer to the
|
||||
* documentation of the latter for more details.
|
||||
*/
|
||||
class dispatcher {
|
||||
using event_family = family<struct internal_dispatcher_event_family>;
|
||||
|
||||
template<typename Class, typename Event>
|
||||
using instance_type = typename sigh<void(const Event &)>::template instance_type<Class>;
|
||||
|
||||
struct base_wrapper {
|
||||
virtual ~base_wrapper() = default;
|
||||
struct basic_pool {
|
||||
virtual ~basic_pool() = default;
|
||||
virtual void publish() = 0;
|
||||
virtual void disconnect(void *) = 0;
|
||||
virtual void clear() ENTT_NOEXCEPT = 0;
|
||||
[[nodiscard]] virtual id_type type_id() const ENTT_NOEXCEPT = 0;
|
||||
};
|
||||
|
||||
template<typename Event>
|
||||
struct signal_wrapper: base_wrapper {
|
||||
using signal_type = sigh<void(const Event &)>;
|
||||
struct pool_handler final: basic_pool {
|
||||
using signal_type = sigh<void(Event &)>;
|
||||
using sink_type = typename signal_type::sink_type;
|
||||
|
||||
void publish() override {
|
||||
for(const auto &event: events[current]) {
|
||||
signal.publish(event);
|
||||
const auto length = events.size();
|
||||
|
||||
for(std::size_t pos{}; pos < length; ++pos) {
|
||||
signal.publish(events[pos]);
|
||||
}
|
||||
|
||||
events[current++].clear();
|
||||
current %= std::extent<decltype(events)>::value;
|
||||
events.erase(events.cbegin(), events.cbegin()+length);
|
||||
}
|
||||
|
||||
sink_type sink() ENTT_NOEXCEPT {
|
||||
void disconnect(void *instance) override {
|
||||
sink().disconnect(instance);
|
||||
}
|
||||
|
||||
void clear() ENTT_NOEXCEPT override {
|
||||
events.clear();
|
||||
}
|
||||
|
||||
[[nodiscard]] sink_type sink() ENTT_NOEXCEPT {
|
||||
return entt::sink{signal};
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void trigger(Args &&... args) {
|
||||
signal.publish({ std::forward<Args>(args)... });
|
||||
Event instance{std::forward<Args>(args)...};
|
||||
signal.publish(instance);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void enqueue(Args &&... args) {
|
||||
events[current].emplace_back(std::forward<Args>(args)...);
|
||||
if constexpr(std::is_aggregate_v<Event>) {
|
||||
events.push_back(Event{std::forward<Args>(args)...});
|
||||
} else {
|
||||
events.emplace_back(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] id_type type_id() const ENTT_NOEXCEPT override {
|
||||
return type_info<Event>::id();
|
||||
}
|
||||
|
||||
private:
|
||||
signal_type signal{};
|
||||
std::vector<Event> events[2];
|
||||
int current{};
|
||||
};
|
||||
|
||||
struct wrapper_data {
|
||||
std::unique_ptr<base_wrapper> wrapper;
|
||||
ENTT_ID_TYPE runtime_type;
|
||||
std::vector<Event> events;
|
||||
};
|
||||
|
||||
template<typename Event>
|
||||
static auto type() ENTT_NOEXCEPT {
|
||||
if constexpr(is_named_type_v<Event>) {
|
||||
return named_type_traits<Event>::value;
|
||||
} else {
|
||||
return event_family::type<Event>;
|
||||
}
|
||||
}
|
||||
[[nodiscard]] pool_handler<Event> & assure() {
|
||||
static_assert(std::is_same_v<Event, std::decay_t<Event>>, "Invalid event type");
|
||||
|
||||
template<typename Event>
|
||||
signal_wrapper<Event> & assure() {
|
||||
const auto wtype = type<Event>();
|
||||
wrapper_data *wdata = nullptr;
|
||||
if constexpr(ENTT_FAST_PATH(has_type_index_v<Event>)) {
|
||||
const auto index = type_index<Event>::value();
|
||||
|
||||
if constexpr(is_named_type_v<Event>) {
|
||||
const auto it = std::find_if(wrappers.begin(), wrappers.end(), [wtype](const auto &candidate) {
|
||||
return candidate.wrapper && candidate.runtime_type == wtype;
|
||||
});
|
||||
|
||||
wdata = (it == wrappers.cend() ? &wrappers.emplace_back() : &(*it));
|
||||
} else {
|
||||
if(!(wtype < wrappers.size())) {
|
||||
wrappers.resize(wtype+1);
|
||||
if(!(index < pools.size())) {
|
||||
pools.resize(index+1u);
|
||||
}
|
||||
|
||||
wdata = &wrappers[wtype];
|
||||
|
||||
if(wdata->wrapper && wdata->runtime_type != wtype) {
|
||||
wrappers.emplace_back();
|
||||
std::swap(wrappers[wtype], wrappers.back());
|
||||
wdata = &wrappers[wtype];
|
||||
if(!pools[index]) {
|
||||
pools[index].reset(new pool_handler<Event>{});
|
||||
}
|
||||
}
|
||||
|
||||
if(!wdata->wrapper) {
|
||||
wdata->wrapper = std::make_unique<signal_wrapper<Event>>();
|
||||
wdata->runtime_type = wtype;
|
||||
return static_cast<pool_handler<Event> &>(*pools[index]);
|
||||
} else {
|
||||
auto it = std::find_if(pools.begin(), pools.end(), [id = type_info<Event>::id()](const auto &cpool) { return id == cpool->type_id(); });
|
||||
return static_cast<pool_handler<Event> &>(it == pools.cend() ? *pools.emplace_back(new pool_handler<Event>{}) : **it);
|
||||
}
|
||||
|
||||
return static_cast<signal_wrapper<Event> &>(*wdata->wrapper);
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Type of sink for the given event. */
|
||||
template<typename Event>
|
||||
using sink_type = typename signal_wrapper<Event>::sink_type;
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object for the given event.
|
||||
*
|
||||
* A sink is an opaque object used to connect listeners to events.
|
||||
*
|
||||
* The function type for a listener is:
|
||||
* The function type for a listener is _compatible_ with:
|
||||
* @code{.cpp}
|
||||
* void(const Event &);
|
||||
* void(Event &);
|
||||
* @endcode
|
||||
*
|
||||
* The order of invocation of the listeners isn't guaranteed.
|
||||
@@ -143,7 +129,7 @@ public:
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
template<typename Event>
|
||||
sink_type<Event> sink() ENTT_NOEXCEPT {
|
||||
[[nodiscard]] auto sink() {
|
||||
return assure<Event>().sink();
|
||||
}
|
||||
|
||||
@@ -205,6 +191,53 @@ public:
|
||||
assure<std::decay_t<Event>>().enqueue(std::forward<Event>(event));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility function to disconnect everything related to a given value
|
||||
* or instance from a dispatcher.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid object that fits the purpose.
|
||||
*/
|
||||
template<typename Type>
|
||||
void disconnect(Type &value_or_instance) {
|
||||
disconnect(&value_or_instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility function to disconnect everything related to a given value
|
||||
* or instance from a dispatcher.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid object that fits the purpose.
|
||||
*/
|
||||
template<typename Type>
|
||||
void disconnect(Type *value_or_instance) {
|
||||
for(auto &&cpool: pools) {
|
||||
if(cpool) {
|
||||
cpool->disconnect(value_or_instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Discards all the events queued so far.
|
||||
*
|
||||
* If no types are provided, the dispatcher will clear all the existing
|
||||
* pools.
|
||||
*
|
||||
* @tparam Event Type of events to discard.
|
||||
*/
|
||||
template<typename... Event>
|
||||
void clear() {
|
||||
if constexpr(sizeof...(Event) == 0) {
|
||||
for(auto &&cpool: pools) {
|
||||
if(cpool) {
|
||||
cpool->clear();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(assure<Event>().clear(), ...);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Delivers all the pending events of the given type.
|
||||
*
|
||||
@@ -227,21 +260,19 @@ public:
|
||||
* to reduce at a minimum the time spent in the bodies of the listeners.
|
||||
*/
|
||||
void update() const {
|
||||
for(auto pos = wrappers.size(); pos; --pos) {
|
||||
auto &wdata = wrappers[pos-1];
|
||||
|
||||
if(wdata.wrapper) {
|
||||
wdata.wrapper->publish();
|
||||
for(auto pos = pools.size(); pos; --pos) {
|
||||
if(auto &&cpool = pools[pos-1]; cpool) {
|
||||
cpool->publish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<wrapper_data> wrappers;
|
||||
std::vector<std::unique_ptr<basic_pool>> pools;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_SIGNAL_DISPATCHER_HPP
|
||||
#endif
|
||||
|
||||
@@ -2,16 +2,17 @@
|
||||
#define ENTT_SIGNAL_EMITTER_HPP
|
||||
|
||||
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/family.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
@@ -29,33 +30,32 @@ namespace entt {
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* Handlers for the type of events are created internally on the fly. It's not
|
||||
* Pools for the type of events are created internally on the fly. It's not
|
||||
* required to specify in advance the full list of accepted types.<br/>
|
||||
* Moreover, whenever an event is published, an emitter provides the listeners
|
||||
* with a reference to itself along with a const 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.
|
||||
* 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.
|
||||
*
|
||||
* @tparam Derived Actual type of emitter that extends the class template.
|
||||
*/
|
||||
template<typename Derived>
|
||||
class emitter {
|
||||
using handler_family = family<struct internal_emitter_handler_family>;
|
||||
|
||||
struct base_handler {
|
||||
virtual ~base_handler() = default;
|
||||
struct basic_pool {
|
||||
virtual ~basic_pool() = default;
|
||||
virtual bool empty() const ENTT_NOEXCEPT = 0;
|
||||
virtual void clear() ENTT_NOEXCEPT = 0;
|
||||
virtual id_type type_id() const ENTT_NOEXCEPT = 0;
|
||||
};
|
||||
|
||||
template<typename Event>
|
||||
struct event_handler: base_handler {
|
||||
using listener_type = std::function<void(const Event &, Derived &)>;
|
||||
struct pool_handler final: basic_pool {
|
||||
using listener_type = std::function<void(Event &, Derived &)>;
|
||||
using element_type = std::pair<bool, listener_type>;
|
||||
using container_type = std::list<element_type>;
|
||||
using connection_type = typename container_type::iterator;
|
||||
|
||||
bool empty() const ENTT_NOEXCEPT override {
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT override {
|
||||
auto pred = [](auto &&element) { return element.first; };
|
||||
|
||||
return std::all_of(once_list.cbegin(), once_list.cend(), pred) &&
|
||||
@@ -64,9 +64,13 @@ class emitter {
|
||||
|
||||
void clear() ENTT_NOEXCEPT override {
|
||||
if(publishing) {
|
||||
auto func = [](auto &&element) { element.first = true; };
|
||||
std::for_each(once_list.begin(), once_list.end(), func);
|
||||
std::for_each(on_list.begin(), on_list.end(), func);
|
||||
for(auto &&element: once_list) {
|
||||
element.first = true;
|
||||
}
|
||||
|
||||
for(auto &&element: on_list) {
|
||||
element.first = true;
|
||||
}
|
||||
} else {
|
||||
once_list.clear();
|
||||
on_list.clear();
|
||||
@@ -81,7 +85,7 @@ class emitter {
|
||||
return on_list.emplace(on_list.cend(), false, std::move(listener));
|
||||
}
|
||||
|
||||
void erase(connection_type conn) ENTT_NOEXCEPT {
|
||||
void erase(connection_type conn) {
|
||||
conn->first = true;
|
||||
|
||||
if(!publishing) {
|
||||
@@ -91,81 +95,66 @@ class emitter {
|
||||
}
|
||||
}
|
||||
|
||||
void publish(const Event &event, Derived &ref) {
|
||||
void publish(Event &event, Derived &ref) {
|
||||
container_type swap_list;
|
||||
once_list.swap(swap_list);
|
||||
|
||||
auto func = [&event, &ref](auto &&element) {
|
||||
return element.first ? void() : element.second(event, ref);
|
||||
};
|
||||
|
||||
publishing = true;
|
||||
|
||||
std::for_each(on_list.rbegin(), on_list.rend(), func);
|
||||
std::for_each(swap_list.rbegin(), swap_list.rend(), func);
|
||||
for(auto &&element: on_list) {
|
||||
element.first ? void() : element.second(event, ref);
|
||||
}
|
||||
|
||||
for(auto &&element: swap_list) {
|
||||
element.first ? void() : element.second(event, ref);
|
||||
}
|
||||
|
||||
publishing = false;
|
||||
|
||||
on_list.remove_if([](auto &&element) { return element.first; });
|
||||
}
|
||||
|
||||
[[nodiscard]] id_type type_id() const ENTT_NOEXCEPT override {
|
||||
return type_info<Event>::id();
|
||||
}
|
||||
|
||||
private:
|
||||
bool publishing{false};
|
||||
container_type once_list{};
|
||||
container_type on_list{};
|
||||
};
|
||||
|
||||
struct handler_data {
|
||||
std::unique_ptr<base_handler> handler;
|
||||
ENTT_ID_TYPE runtime_type;
|
||||
};
|
||||
|
||||
template<typename Event>
|
||||
static auto type() ENTT_NOEXCEPT {
|
||||
if constexpr(is_named_type_v<Event>) {
|
||||
return named_type_traits<Event>::value;
|
||||
[[nodiscard]] const pool_handler<Event> & assure() const {
|
||||
static_assert(std::is_same_v<Event, std::decay_t<Event>>, "Invalid event type");
|
||||
|
||||
if constexpr(ENTT_FAST_PATH(has_type_index_v<Event>)) {
|
||||
const auto index = type_index<Event>::value();
|
||||
|
||||
if(!(index < pools.size())) {
|
||||
pools.resize(index+1u);
|
||||
}
|
||||
|
||||
if(!pools[index]) {
|
||||
pools[index].reset(new pool_handler<Event>{});
|
||||
}
|
||||
|
||||
return static_cast<pool_handler<Event> &>(*pools[index]);
|
||||
} else {
|
||||
return handler_family::type<Event>;
|
||||
auto it = std::find_if(pools.begin(), pools.end(), [id = type_info<Event>::id()](const auto &cpool) { return id == cpool->type_id(); });
|
||||
return static_cast<pool_handler<Event> &>(it == pools.cend() ? *pools.emplace_back(new pool_handler<Event>{}) : **it);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Event>
|
||||
event_handler<Event> * assure() const ENTT_NOEXCEPT {
|
||||
const auto htype = type<Event>();
|
||||
handler_data *hdata = nullptr;
|
||||
|
||||
if constexpr(is_named_type_v<Event>) {
|
||||
const auto it = std::find_if(handlers.begin(), handlers.end(), [htype](const auto &candidate) {
|
||||
return candidate.handler && candidate.runtime_type == htype;
|
||||
});
|
||||
|
||||
hdata = (it == handlers.cend() ? &handlers.emplace_back() : &(*it));
|
||||
} else {
|
||||
if(!(htype < handlers.size())) {
|
||||
handlers.resize(htype+1);
|
||||
}
|
||||
|
||||
hdata = &handlers[htype];
|
||||
|
||||
if(hdata->handler && hdata->runtime_type != htype) {
|
||||
handlers.emplace_back();
|
||||
std::swap(handlers[htype], handlers.back());
|
||||
hdata = &handlers[htype];
|
||||
}
|
||||
}
|
||||
|
||||
if(!hdata->handler) {
|
||||
hdata->handler = std::make_unique<event_handler<Event>>();
|
||||
hdata->runtime_type = htype;
|
||||
}
|
||||
|
||||
return static_cast<event_handler<Event> *>(hdata->handler.get());
|
||||
[[nodiscard]] pool_handler<Event> & assure() {
|
||||
return const_cast<pool_handler<Event> &>(std::as_const(*this).template assure<Event>());
|
||||
}
|
||||
|
||||
public:
|
||||
/** @brief Type of listeners accepted for the given event. */
|
||||
template<typename Event>
|
||||
using listener = typename event_handler<Event>::listener_type;
|
||||
using listener = typename pool_handler<Event>::listener_type;
|
||||
|
||||
/**
|
||||
* @brief Generic connection type for events.
|
||||
@@ -177,28 +166,28 @@ public:
|
||||
* @tparam Event Type of event for which the connection is created.
|
||||
*/
|
||||
template<typename Event>
|
||||
struct connection: private event_handler<Event>::connection_type {
|
||||
struct connection: private pool_handler<Event>::connection_type {
|
||||
/** @brief Event emitters are friend classes of connections. */
|
||||
friend class emitter;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
connection() ENTT_NOEXCEPT = default;
|
||||
connection() = default;
|
||||
|
||||
/**
|
||||
* @brief Creates a connection that wraps its underlying instance.
|
||||
* @param conn A connection object to wrap.
|
||||
*/
|
||||
connection(typename event_handler<Event>::connection_type conn)
|
||||
: event_handler<Event>::connection_type{std::move(conn)}
|
||||
connection(typename pool_handler<Event>::connection_type conn)
|
||||
: pool_handler<Event>::connection_type{std::move(conn)}
|
||||
{}
|
||||
};
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
emitter() ENTT_NOEXCEPT = default;
|
||||
emitter() = default;
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
virtual ~emitter() ENTT_NOEXCEPT {
|
||||
static_assert(std::is_base_of_v<emitter<Derived>, Derived>);
|
||||
virtual ~emitter() {
|
||||
static_assert(std::is_base_of_v<emitter<Derived>, Derived>, "Incorrect use of the class template");
|
||||
}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
@@ -220,7 +209,8 @@ public:
|
||||
*/
|
||||
template<typename Event, typename... Args>
|
||||
void publish(Args &&... args) {
|
||||
assure<Event>()->publish({ std::forward<Args>(args)... }, *static_cast<Derived *>(this));
|
||||
Event instance{std::forward<Args>(args)...};
|
||||
assure<Event>().publish(instance, *static_cast<Derived *>(this));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,7 +222,7 @@ public:
|
||||
* to be used later to disconnect the listener if required.
|
||||
*
|
||||
* The listener is as a callable object that can be moved and the type of
|
||||
* which is `void(const Event &, Derived &)`.
|
||||
* which is _compatible_ with `void(Event &, Derived &)`.
|
||||
*
|
||||
* @note
|
||||
* Whenever an event is emitted, the emitter provides the listener with a
|
||||
@@ -245,7 +235,7 @@ public:
|
||||
*/
|
||||
template<typename Event>
|
||||
connection<Event> on(listener<Event> instance) {
|
||||
return assure<Event>()->on(std::move(instance));
|
||||
return assure<Event>().on(std::move(instance));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,7 +247,7 @@ public:
|
||||
* to be used later to disconnect the listener if required.
|
||||
*
|
||||
* The listener is as a callable object that can be moved and the type of
|
||||
* which is `void(const Event &, Derived &)`.
|
||||
* which is _compatible_ with `void(Event &, Derived &)`.
|
||||
*
|
||||
* @note
|
||||
* Whenever an event is emitted, the emitter provides the listener with a
|
||||
@@ -270,7 +260,7 @@ public:
|
||||
*/
|
||||
template<typename Event>
|
||||
connection<Event> once(listener<Event> instance) {
|
||||
return assure<Event>()->once(std::move(instance));
|
||||
return assure<Event>().once(std::move(instance));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,8 +273,8 @@ public:
|
||||
* @param conn A valid connection.
|
||||
*/
|
||||
template<typename Event>
|
||||
void erase(connection<Event> conn) ENTT_NOEXCEPT {
|
||||
assure<Event>()->erase(std::move(conn));
|
||||
void erase(connection<Event> conn) {
|
||||
assure<Event>().erase(std::move(conn));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -296,8 +286,8 @@ public:
|
||||
* @tparam Event Type of event to reset.
|
||||
*/
|
||||
template<typename Event>
|
||||
void clear() ENTT_NOEXCEPT {
|
||||
assure<Event>()->clear();
|
||||
void clear() {
|
||||
assure<Event>().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -307,9 +297,11 @@ public:
|
||||
* results in undefined behavior.
|
||||
*/
|
||||
void clear() ENTT_NOEXCEPT {
|
||||
std::for_each(handlers.begin(), handlers.end(), [](auto &&hdata) {
|
||||
return hdata.handler ? hdata.handler->clear() : void();
|
||||
});
|
||||
for(auto &&cpool: pools) {
|
||||
if(cpool) {
|
||||
cpool->clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -318,26 +310,26 @@ public:
|
||||
* @return True if there are no listeners registered, false otherwise.
|
||||
*/
|
||||
template<typename Event>
|
||||
bool empty() const ENTT_NOEXCEPT {
|
||||
return assure<Event>()->empty();
|
||||
[[nodiscard]] bool empty() const {
|
||||
return assure<Event>().empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if there are listeners registered with the event emitter.
|
||||
* @return True if there are no listeners registered, false otherwise.
|
||||
*/
|
||||
bool empty() const ENTT_NOEXCEPT {
|
||||
return std::all_of(handlers.cbegin(), handlers.cend(), [](auto &&hdata) {
|
||||
return !hdata.handler || hdata.handler->empty();
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return std::all_of(pools.cbegin(), pools.cend(), [](auto &&cpool) {
|
||||
return !cpool || cpool->empty();
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::vector<handler_data> handlers{};
|
||||
mutable std::vector<std::unique_ptr<basic_pool>> pools{};
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_SIGNAL_EMITTER_HPP
|
||||
#endif
|
||||
|
||||
@@ -2,21 +2,30 @@
|
||||
#define ENTT_SIGNAL_FWD_HPP
|
||||
|
||||
|
||||
#include "../config/config.h"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/*! @class delegate */
|
||||
template<typename>
|
||||
class delegate;
|
||||
|
||||
/*! @class sink */
|
||||
|
||||
class dispatcher;
|
||||
|
||||
|
||||
template<typename>
|
||||
class emitter;
|
||||
|
||||
|
||||
class connection;
|
||||
|
||||
|
||||
struct scoped_connection;
|
||||
|
||||
|
||||
template<typename>
|
||||
class sink;
|
||||
|
||||
/*! @class sigh */
|
||||
|
||||
template<typename>
|
||||
class sigh;
|
||||
|
||||
@@ -24,4 +33,4 @@ class sigh;
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_SIGNAL_FWD_HPP
|
||||
#endif
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
#define ENTT_SIGNAL_SIGH_HPP
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
@@ -61,9 +62,9 @@ class sigh<Ret(Args...)> {
|
||||
|
||||
public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename std::vector<delegate<Ret(Args...)>>::size_type;
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Sink type. */
|
||||
using sink_type = entt::sink<Ret(Args...)>;
|
||||
using sink_type = sink<Ret(Args...)>;
|
||||
|
||||
/**
|
||||
* @brief Instance type when it comes to connecting member functions.
|
||||
@@ -76,7 +77,7 @@ public:
|
||||
* @brief Number of listeners connected to the signal.
|
||||
* @return Number of listeners currently connected.
|
||||
*/
|
||||
size_type size() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
|
||||
return calls.size();
|
||||
}
|
||||
|
||||
@@ -84,7 +85,7 @@ public:
|
||||
* @brief Returns false if at least a listener is connected to the signal.
|
||||
* @return True if the signal has no listeners connected, false otherwise.
|
||||
*/
|
||||
bool empty() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return calls.empty();
|
||||
}
|
||||
|
||||
@@ -96,8 +97,8 @@ public:
|
||||
* @param args Arguments to use to invoke listeners.
|
||||
*/
|
||||
void publish(Args... args) const {
|
||||
for(auto pos = calls.size(); pos; --pos) {
|
||||
calls[pos-1](args...);
|
||||
for(auto &&call: std::as_const(calls)) {
|
||||
call(args...);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,22 +118,20 @@ public:
|
||||
*/
|
||||
template<typename Func>
|
||||
void collect(Func func, Args... args) const {
|
||||
bool stop = false;
|
||||
|
||||
for(auto pos = calls.size(); pos && !stop; --pos) {
|
||||
for(auto &&call: calls) {
|
||||
if constexpr(std::is_void_v<Ret>) {
|
||||
if constexpr(std::is_invocable_r_v<bool, Func>) {
|
||||
calls[pos-1](args...);
|
||||
stop = func();
|
||||
call(args...);
|
||||
if(func()) { break; }
|
||||
} else {
|
||||
calls[pos-1](args...);
|
||||
call(args...);
|
||||
func();
|
||||
}
|
||||
} else {
|
||||
if constexpr(std::is_invocable_r_v<bool, Func, Ret>) {
|
||||
stop = func(calls[pos-1](args...));
|
||||
if(func(call(args...))) { break; }
|
||||
} else {
|
||||
func(calls[pos-1](args...));
|
||||
func(call(args...));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,46 +159,14 @@ class connection {
|
||||
{}
|
||||
|
||||
public:
|
||||
/*! Default constructor. */
|
||||
/*! @brief Default constructor. */
|
||||
connection() = default;
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
connection(const connection &) = default;
|
||||
|
||||
/**
|
||||
* @brief Default move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
connection(connection &&other)
|
||||
: connection{}
|
||||
{
|
||||
std::swap(disconnect, other.disconnect);
|
||||
std::swap(signal, other.signal);
|
||||
}
|
||||
|
||||
/*! @brief Default copy assignment operator. @return This connection. */
|
||||
connection & operator=(const connection &) = default;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This connection.
|
||||
*/
|
||||
connection & operator=(connection &&other) {
|
||||
if(this != &other) {
|
||||
auto tmp{std::move(other)};
|
||||
disconnect = tmp.disconnect;
|
||||
signal = tmp.signal;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a connection is properly initialized.
|
||||
* @return True if the connection is properly initialized, false otherwise.
|
||||
*/
|
||||
explicit operator bool() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return static_cast<bool>(disconnect);
|
||||
}
|
||||
|
||||
@@ -226,27 +193,24 @@ private:
|
||||
* A scoped connection automatically breaks the link between the two objects
|
||||
* when it goes out of scope.
|
||||
*/
|
||||
struct scoped_connection: private connection {
|
||||
/*! Default constructor. */
|
||||
struct scoped_connection {
|
||||
/*! @brief Default constructor. */
|
||||
scoped_connection() = default;
|
||||
|
||||
/**
|
||||
* @brief Constructs a scoped connection from a basic connection.
|
||||
* @param conn A valid connection object.
|
||||
* @param other A valid connection object.
|
||||
*/
|
||||
scoped_connection(const connection &conn)
|
||||
: connection{conn}
|
||||
scoped_connection(const connection &other)
|
||||
: conn{other}
|
||||
{}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
scoped_connection(const scoped_connection &) = delete;
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
scoped_connection(scoped_connection &&) = default;
|
||||
|
||||
/*! @brief Automatically breaks the link on destruction. */
|
||||
~scoped_connection() {
|
||||
connection::release();
|
||||
conn.release();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,33 +220,30 @@ struct scoped_connection: private connection {
|
||||
scoped_connection & operator=(const scoped_connection &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @brief Acquires a connection.
|
||||
* @param other The connection object to acquire.
|
||||
* @return This scoped connection.
|
||||
*/
|
||||
scoped_connection & operator=(scoped_connection &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Copies a connection.
|
||||
* @param other The connection object to copy.
|
||||
* @return This scoped connection.
|
||||
*/
|
||||
scoped_connection & operator=(const connection &other) {
|
||||
static_cast<connection &>(*this) = other;
|
||||
scoped_connection & operator=(connection other) {
|
||||
conn = std::move(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Moves a connection.
|
||||
* @param other The connection object to move.
|
||||
* @return This scoped connection.
|
||||
* @brief Checks whether a scoped connection is properly initialized.
|
||||
* @return True if the connection is properly initialized, false otherwise.
|
||||
*/
|
||||
scoped_connection & operator=(connection &&other) {
|
||||
static_cast<connection &>(*this) = std::move(other);
|
||||
return *this;
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return static_cast<bool>(conn);
|
||||
}
|
||||
|
||||
using connection::operator bool;
|
||||
using connection::release;
|
||||
/*! @brief Breaks the connection. */
|
||||
void release() {
|
||||
conn.release();
|
||||
}
|
||||
|
||||
private:
|
||||
connection conn;
|
||||
};
|
||||
|
||||
|
||||
@@ -296,6 +257,10 @@ struct scoped_connection: private connection {
|
||||
* The clear separation between a signal and a sink permits to store the former
|
||||
* as private data member without exposing the publish functionality to the
|
||||
* users of the class.
|
||||
*
|
||||
* @warning
|
||||
* Lifetime of a sink must not overcome that of the signal to which it refers.
|
||||
* In any other case, attempting to use a sink results in undefined behavior.
|
||||
*
|
||||
* @tparam Ret Return type of a function type.
|
||||
* @tparam Args Types of arguments of a function type.
|
||||
@@ -303,15 +268,16 @@ struct scoped_connection: private connection {
|
||||
template<typename Ret, typename... Args>
|
||||
class sink<Ret(Args...)> {
|
||||
using signal_type = sigh<Ret(Args...)>;
|
||||
using difference_type = typename std::iterator_traits<typename decltype(signal_type::calls)::iterator>::difference_type;
|
||||
|
||||
template<auto Candidate, typename Type>
|
||||
static void release(Type &value_or_instance, void *signal) {
|
||||
static void release(Type value_or_instance, void *signal) {
|
||||
sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>(value_or_instance);
|
||||
}
|
||||
|
||||
template<auto Function>
|
||||
template<auto Candidate>
|
||||
static void release(void *signal) {
|
||||
sink{*static_cast<signal_type *>(signal)}.disconnect<Function>();
|
||||
sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>();
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -320,100 +286,207 @@ public:
|
||||
* @param ref A valid reference to a signal object.
|
||||
*/
|
||||
sink(sigh<Ret(Args...)> &ref) ENTT_NOEXCEPT
|
||||
: signal{&ref}
|
||||
: offset{},
|
||||
signal{&ref}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Returns false if at least a listener is connected to the sink.
|
||||
* @return True if the sink has no listeners connected, false otherwise.
|
||||
*/
|
||||
bool empty() const ENTT_NOEXCEPT {
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return signal->calls.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects a free function to a signal.
|
||||
*
|
||||
* The signal handler performs checks to avoid multiple connections for free
|
||||
* functions.
|
||||
*
|
||||
* @brief Returns a sink that connects before a given free function or an
|
||||
* unbound member.
|
||||
* @tparam Function A valid free function pointer.
|
||||
* @return A properly initialized connection object.
|
||||
* @return A properly initialized sink object.
|
||||
*/
|
||||
template<auto Function>
|
||||
[[nodiscard]] sink before() {
|
||||
delegate<Ret(Args...)> call{};
|
||||
call.template connect<Function>();
|
||||
|
||||
const auto &calls = signal->calls;
|
||||
const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call));
|
||||
|
||||
sink other{*this};
|
||||
other.offset = std::distance(it, calls.cend());
|
||||
return other;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink that connects before a free function with payload
|
||||
* or a bound member.
|
||||
* @tparam Candidate Member or free function to look for.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid object that fits the purpose.
|
||||
* @return A properly initialized sink object.
|
||||
*/
|
||||
template<auto Candidate, typename Type>
|
||||
[[nodiscard]] sink before(Type &&value_or_instance) {
|
||||
delegate<Ret(Args...)> call{};
|
||||
call.template connect<Candidate>(value_or_instance);
|
||||
|
||||
const auto &calls = signal->calls;
|
||||
const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call));
|
||||
|
||||
sink other{*this};
|
||||
other.offset = std::distance(it, calls.cend());
|
||||
return other;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink that connects before a given instance or specific
|
||||
* payload.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid object that fits the purpose.
|
||||
* @return A properly initialized sink object.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] sink before(Type &value_or_instance) {
|
||||
return before(&value_or_instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink that connects before a given instance or specific
|
||||
* payload.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid pointer that fits the purpose.
|
||||
* @return A properly initialized sink object.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] sink before(Type *value_or_instance) {
|
||||
sink other{*this};
|
||||
|
||||
if(value_or_instance) {
|
||||
const auto &calls = signal->calls;
|
||||
const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) {
|
||||
return delegate.instance() == value_or_instance;
|
||||
});
|
||||
|
||||
other.offset = std::distance(it, calls.cend());
|
||||
}
|
||||
|
||||
return other;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink that connects before anything else.
|
||||
* @return A properly initialized sink object.
|
||||
*/
|
||||
[[nodiscard]] sink before() {
|
||||
sink other{*this};
|
||||
other.offset = signal->calls.size();
|
||||
return other;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects a free function or an unbound member to a signal.
|
||||
*
|
||||
* The signal handler performs checks to avoid multiple connections for the
|
||||
* same function.
|
||||
*
|
||||
* @tparam Candidate Function or member to connect to the signal.
|
||||
* @return A properly initialized connection object.
|
||||
*/
|
||||
template<auto Candidate>
|
||||
connection connect() {
|
||||
disconnect<Function>();
|
||||
disconnect<Candidate>();
|
||||
|
||||
delegate<Ret(Args...)> call{};
|
||||
call.template connect<Candidate>();
|
||||
signal->calls.insert(signal->calls.end() - offset, std::move(call));
|
||||
|
||||
delegate<void(void *)> conn{};
|
||||
conn.template connect<&release<Function>>();
|
||||
signal->calls.emplace_back(delegate<Ret(Args...)>{connect_arg<Function>});
|
||||
conn.template connect<&release<Candidate>>();
|
||||
return { std::move(conn), signal };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connects a member function or a free function with payload to a
|
||||
* @brief Connects a free function with payload or a bound member to a
|
||||
* signal.
|
||||
*
|
||||
* The signal isn't responsible for the connected object or the payload.
|
||||
* Users must always guarantee that the lifetime of the instance overcomes
|
||||
* the one of the delegate. On the other side, the signal handler performs
|
||||
* the one of the signal. On the other side, the signal handler performs
|
||||
* checks to avoid multiple connections for the same function.<br/>
|
||||
* When used to connect a free function with payload, its signature must be
|
||||
* such that the instance is the first argument before the ones used to
|
||||
* define the delegate itself.
|
||||
* define the signal itself.
|
||||
*
|
||||
* @tparam Candidate Member or free function to connect to the signal.
|
||||
* @tparam Candidate Function or member to connect to the signal.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid reference that fits the purpose.
|
||||
* @param value_or_instance A valid object that fits the purpose.
|
||||
* @return A properly initialized connection object.
|
||||
*/
|
||||
template<auto Candidate, typename Type>
|
||||
connection connect(Type &value_or_instance) {
|
||||
connection connect(Type &&value_or_instance) {
|
||||
disconnect<Candidate>(value_or_instance);
|
||||
|
||||
delegate<Ret(Args...)> call{};
|
||||
call.template connect<Candidate>(value_or_instance);
|
||||
signal->calls.insert(signal->calls.end() - offset, std::move(call));
|
||||
|
||||
delegate<void(void *)> conn{};
|
||||
conn.template connect<&sink::release<Candidate, Type>>(value_or_instance);
|
||||
signal->calls.emplace_back(delegate<Ret(Args...)>{connect_arg<Candidate>, value_or_instance});
|
||||
conn.template connect<&release<Candidate, Type>>(value_or_instance);
|
||||
return { std::move(conn), signal };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnects a free function from a signal.
|
||||
* @tparam Function A valid free function pointer.
|
||||
* @brief Disconnects a free function or an unbound member from a signal.
|
||||
* @tparam Candidate Function or member to disconnect from the signal.
|
||||
*/
|
||||
template<auto Function>
|
||||
template<auto Candidate>
|
||||
void disconnect() {
|
||||
auto &calls = signal->calls;
|
||||
delegate<Ret(Args...)> delegate{};
|
||||
delegate.template connect<Function>();
|
||||
calls.erase(std::remove(calls.begin(), calls.end(), delegate), calls.end());
|
||||
delegate<Ret(Args...)> call{};
|
||||
call.template connect<Candidate>();
|
||||
calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnects a member function or a free function with payload from
|
||||
* a signal.
|
||||
* @tparam Candidate Member or free function to disconnect from the signal.
|
||||
* @brief Disconnects a free function with payload or a bound member from a
|
||||
* signal.
|
||||
* @tparam Candidate Function or member to disconnect from the signal.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid reference that fits the purpose.
|
||||
* @param value_or_instance A valid object that fits the purpose.
|
||||
*/
|
||||
template<auto Candidate, typename Type>
|
||||
void disconnect(Type &value_or_instance) {
|
||||
void disconnect(Type &&value_or_instance) {
|
||||
auto &calls = signal->calls;
|
||||
delegate<Ret(Args...)> delegate{};
|
||||
delegate.template connect<Candidate>(value_or_instance);
|
||||
calls.erase(std::remove(calls.begin(), calls.end(), delegate), calls.end());
|
||||
delegate<Ret(Args...)> call{};
|
||||
call.template connect<Candidate>(value_or_instance);
|
||||
calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnects member functions or free functions based on an
|
||||
* instance or specific payload.
|
||||
* @brief Disconnects free functions with payload or bound members from a
|
||||
* signal.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid reference that fits the purpose.
|
||||
* @param value_or_instance A valid object that fits the purpose.
|
||||
*/
|
||||
template<typename Type>
|
||||
void disconnect(const Type &value_or_instance) {
|
||||
auto &calls = signal->calls;
|
||||
calls.erase(std::remove_if(calls.begin(), calls.end(), [&value_or_instance](const auto &delegate) {
|
||||
return delegate.instance() == &value_or_instance;
|
||||
}), calls.end());
|
||||
void disconnect(Type &value_or_instance) {
|
||||
disconnect(&value_or_instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnects free functions with payload or bound members from a
|
||||
* signal.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid object that fits the purpose.
|
||||
*/
|
||||
template<typename Type>
|
||||
void disconnect(Type *value_or_instance) {
|
||||
if(value_or_instance) {
|
||||
auto &calls = signal->calls;
|
||||
calls.erase(std::remove_if(calls.begin(), calls.end(), [value_or_instance](const auto &delegate) {
|
||||
return delegate.instance() == value_or_instance;
|
||||
}), calls.end());
|
||||
}
|
||||
}
|
||||
|
||||
/*! @brief Disconnects all the listeners from a signal. */
|
||||
@@ -422,6 +495,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
difference_type offset;
|
||||
signal_type *signal;
|
||||
};
|
||||
|
||||
@@ -442,4 +516,4 @@ sink(sigh<Ret(Args...)> &) ENTT_NOEXCEPT -> sink<Ret(Args...)>;
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_SIGNAL_SIGH_HPP
|
||||
#endif
|
||||
|
||||
@@ -2,128 +2,209 @@
|
||||
# Tests configuration
|
||||
#
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
if(FIND_GTEST_PACKAGE)
|
||||
find_package(GTest REQUIRED)
|
||||
else()
|
||||
FetchContent_Declare(
|
||||
googletest
|
||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||
GIT_TAG master
|
||||
GIT_SHALLOW 1
|
||||
)
|
||||
|
||||
FetchContent_GetProperties(googletest)
|
||||
|
||||
if(NOT googletest_POPULATED)
|
||||
FetchContent_Populate(googletest)
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
add_library(GTest::Main ALIAS gtest_main)
|
||||
|
||||
target_compile_features(gtest PUBLIC cxx_std_17)
|
||||
target_compile_features(gtest_main PUBLIC cxx_std_17)
|
||||
target_compile_features(gmock PUBLIC cxx_std_17)
|
||||
target_compile_features(gmock_main PUBLIC cxx_std_17)
|
||||
endif()
|
||||
|
||||
include_directories($<TARGET_PROPERTY:EnTT,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||
add_compile_options($<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_OPTIONS>)
|
||||
|
||||
macro(SETUP_LIBRARY_TARGET LIB_TARGET)
|
||||
set_target_properties(${LIB_TARGET} PROPERTIES CXX_EXTENSIONS OFF)
|
||||
target_compile_definitions(${LIB_TARGET} PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_DEFINITIONS>)
|
||||
target_compile_features(${LIB_TARGET} PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_FEATURES>)
|
||||
target_compile_options(${LIB_TARGET} PRIVATE $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-pedantic -Wall>)
|
||||
target_compile_options(${LIB_TARGET} PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/EHsc>)
|
||||
endmacro()
|
||||
function(SETUP_TARGET TARGET_NAME)
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES CXX_EXTENSIONS OFF)
|
||||
target_link_libraries(${TARGET_NAME} PRIVATE EnTT)
|
||||
|
||||
target_compile_options(
|
||||
${TARGET_NAME}
|
||||
PRIVATE
|
||||
$<$<NOT:$<PLATFORM_ID:Windows>>:-pedantic -fvisibility=hidden -Wall -Wshadow -Wno-deprecated-declarations>
|
||||
$<$<PLATFORM_ID:Windows>:/EHsc /W1 /wd4996 /w14800>
|
||||
)
|
||||
|
||||
target_compile_options(
|
||||
${TARGET_NAME}
|
||||
PRIVATE
|
||||
$<$<AND:$<CONFIG:Debug>,$<NOT:$<PLATFORM_ID:Windows>>>:-O0 -g>
|
||||
$<$<AND:$<CONFIG:Release>,$<NOT:$<PLATFORM_ID:Windows>>>:-O2>
|
||||
$<$<AND:$<CONFIG:Debug>,$<PLATFORM_ID:Windows>>:/Od>
|
||||
$<$<AND:$<CONFIG:Release>,$<PLATFORM_ID:Windows>>:/O2>
|
||||
)
|
||||
|
||||
target_compile_definitions(${TARGET_NAME} PRIVATE ENTT_STANDALONE ${ARGN})
|
||||
endfunction()
|
||||
|
||||
add_library(odr OBJECT odr.cpp)
|
||||
SETUP_LIBRARY_TARGET(odr)
|
||||
SETUP_TARGET(odr)
|
||||
|
||||
macro(SETUP_AND_ADD_TEST TEST_NAME TEST_SOURCE)
|
||||
add_executable(${TEST_NAME} $<TARGET_OBJECTS:odr> ${TEST_SOURCE})
|
||||
set_target_properties(${TEST_NAME} PROPERTIES CXX_EXTENSIONS OFF)
|
||||
target_link_libraries(${TEST_NAME} PRIVATE EnTT GTest::Main Threads::Threads)
|
||||
target_compile_definitions(${TEST_NAME} PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_DEFINITIONS>)
|
||||
target_compile_features(${TEST_NAME} PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_FEATURES>)
|
||||
target_compile_options(${TEST_NAME} PRIVATE $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-pedantic -Wall>)
|
||||
target_compile_options(${TEST_NAME} PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/EHsc>)
|
||||
function(SETUP_BASIC_TEST TEST_NAME TEST_SOURCES)
|
||||
add_executable(${TEST_NAME} $<TARGET_OBJECTS:odr> ${TEST_SOURCES})
|
||||
target_link_libraries(${TEST_NAME} PRIVATE GTest::Main Threads::Threads)
|
||||
SETUP_TARGET(${TEST_NAME} ${ARGN})
|
||||
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
|
||||
endmacro()
|
||||
endfunction()
|
||||
|
||||
function(SETUP_LIB_TEST TEST_NAME)
|
||||
add_library(_${TEST_NAME} SHARED $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/lib.cpp)
|
||||
SETUP_TARGET(_${TEST_NAME} ENTT_API_EXPORT)
|
||||
SETUP_BASIC_TEST(lib_${TEST_NAME} lib/${TEST_NAME}/main.cpp ENTT_API_IMPORT)
|
||||
target_link_libraries(lib_${TEST_NAME} PRIVATE _${TEST_NAME})
|
||||
endfunction()
|
||||
|
||||
function(SETUP_PLUGIN_TEST TEST_NAME)
|
||||
add_library(_${TEST_NAME} MODULE $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/plugin.cpp)
|
||||
SETUP_TARGET(_${TEST_NAME} NOMINMAX ${ARGVN})
|
||||
SETUP_BASIC_TEST(lib_${TEST_NAME} lib/${TEST_NAME}/main.cpp NOMINMAX PLUGIN="$<TARGET_FILE:_${TEST_NAME}>" ${ARGVN})
|
||||
target_include_directories(_${TEST_NAME} PRIVATE ${cr_INCLUDE_DIR})
|
||||
target_include_directories(lib_${TEST_NAME} PRIVATE ${cr_INCLUDE_DIR})
|
||||
target_link_libraries(lib_${TEST_NAME} PRIVATE ${CMAKE_DL_LIBS})
|
||||
endfunction()
|
||||
|
||||
# Test benchmark
|
||||
|
||||
if(BUILD_BENCHMARK)
|
||||
SETUP_AND_ADD_TEST(benchmark benchmark/benchmark.cpp)
|
||||
SETUP_BASIC_TEST(benchmark benchmark/benchmark.cpp)
|
||||
endif()
|
||||
|
||||
# Test example
|
||||
|
||||
if(BUILD_EXAMPLE)
|
||||
SETUP_BASIC_TEST(custom_identifier example/custom_identifier.cpp)
|
||||
endif()
|
||||
|
||||
# Test lib
|
||||
|
||||
if(BUILD_LIB)
|
||||
add_library(a_module_shared SHARED lib/a_module.cpp)
|
||||
add_library(a_module_static STATIC lib/a_module.cpp)
|
||||
add_library(another_module_shared SHARED lib/another_module.cpp)
|
||||
add_library(another_module_static STATIC lib/another_module.cpp)
|
||||
FetchContent_Declare(
|
||||
cr
|
||||
GIT_REPOSITORY https://github.com/fungos/cr.git
|
||||
GIT_TAG master
|
||||
GIT_SHALLOW 1
|
||||
)
|
||||
|
||||
SETUP_LIBRARY_TARGET(a_module_shared)
|
||||
SETUP_LIBRARY_TARGET(a_module_static)
|
||||
SETUP_LIBRARY_TARGET(another_module_shared)
|
||||
SETUP_LIBRARY_TARGET(another_module_static)
|
||||
FetchContent_GetProperties(cr)
|
||||
|
||||
SETUP_AND_ADD_TEST(lib_shared lib/lib.cpp)
|
||||
target_link_libraries(lib_shared PRIVATE a_module_shared another_module_shared)
|
||||
if(NOT cr_POPULATED)
|
||||
FetchContent_Populate(cr)
|
||||
set(cr_INCLUDE_DIR ${cr_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
SETUP_AND_ADD_TEST(lib_static lib/lib.cpp)
|
||||
target_link_libraries(lib_static PRIVATE a_module_static another_module_static)
|
||||
endif()
|
||||
SETUP_LIB_TEST(dispatcher)
|
||||
SETUP_LIB_TEST(emitter)
|
||||
SETUP_LIB_TEST(meta)
|
||||
SETUP_LIB_TEST(registry)
|
||||
|
||||
# Test mod
|
||||
SETUP_PLUGIN_TEST(dispatcher_plugin)
|
||||
SETUP_PLUGIN_TEST(emitter_plugin)
|
||||
SETUP_PLUGIN_TEST(meta_plugin)
|
||||
SETUP_PLUGIN_TEST(registry_plugin)
|
||||
|
||||
if(BUILD_MOD)
|
||||
set(DUKTAPE_DEPS_DIR ${EnTT_SOURCE_DIR}/deps/duktape)
|
||||
configure_file(${EnTT_SOURCE_DIR}/cmake/in/duktape.in ${DUKTAPE_DEPS_DIR}/CMakeLists.txt)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${DUKTAPE_DEPS_DIR})
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${DUKTAPE_DEPS_DIR})
|
||||
set(DUKTAPE_SRC_DIR ${DUKTAPE_DEPS_DIR}/src/src)
|
||||
|
||||
set(MOD_TEST_SOURCE ${DUKTAPE_SRC_DIR}/duktape.c mod/mod.cpp)
|
||||
SETUP_AND_ADD_TEST(mod "${MOD_TEST_SOURCE}")
|
||||
target_include_directories(mod PRIVATE ${DUKTAPE_SRC_DIR})
|
||||
SETUP_PLUGIN_TEST(meta_plugin_std ENTT_STANDARD_CPP)
|
||||
endif()
|
||||
|
||||
# Test snapshot
|
||||
|
||||
if(BUILD_SNAPSHOT)
|
||||
set(CEREAL_DEPS_DIR ${EnTT_SOURCE_DIR}/deps/cereal)
|
||||
configure_file(${EnTT_SOURCE_DIR}/cmake/in/cereal.in ${CEREAL_DEPS_DIR}/CMakeLists.txt)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${CEREAL_DEPS_DIR})
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${CEREAL_DEPS_DIR})
|
||||
set(CEREAL_SRC_DIR ${CEREAL_DEPS_DIR}/src/include)
|
||||
FetchContent_Declare(
|
||||
cereal
|
||||
GIT_REPOSITORY https://github.com/USCiLab/cereal.git
|
||||
GIT_TAG v1.2.2
|
||||
GIT_SHALLOW 1
|
||||
)
|
||||
|
||||
SETUP_AND_ADD_TEST(cereal snapshot/snapshot.cpp)
|
||||
target_include_directories(cereal PRIVATE ${CEREAL_SRC_DIR})
|
||||
FetchContent_GetProperties(cereal)
|
||||
|
||||
if(NOT cereal_POPULATED)
|
||||
FetchContent_Populate(cereal)
|
||||
set(cereal_INCLUDE_DIR ${cereal_SOURCE_DIR}/include)
|
||||
endif()
|
||||
|
||||
SETUP_BASIC_TEST(cereal snapshot/snapshot.cpp)
|
||||
target_include_directories(cereal PRIVATE ${cereal_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
# Test core
|
||||
|
||||
SETUP_AND_ADD_TEST(algorithm entt/core/algorithm.cpp)
|
||||
SETUP_AND_ADD_TEST(family entt/core/family.cpp)
|
||||
SETUP_AND_ADD_TEST(hashed_string entt/core/hashed_string.cpp)
|
||||
SETUP_AND_ADD_TEST(ident entt/core/ident.cpp)
|
||||
SETUP_AND_ADD_TEST(monostate entt/core/monostate.cpp)
|
||||
SETUP_AND_ADD_TEST(type_traits entt/core/type_traits.cpp)
|
||||
SETUP_AND_ADD_TEST(utility entt/core/utility.cpp)
|
||||
SETUP_BASIC_TEST(algorithm entt/core/algorithm.cpp)
|
||||
SETUP_BASIC_TEST(family entt/core/family.cpp)
|
||||
SETUP_BASIC_TEST(hashed_string entt/core/hashed_string.cpp)
|
||||
SETUP_BASIC_TEST(ident entt/core/ident.cpp)
|
||||
SETUP_BASIC_TEST(monostate entt/core/monostate.cpp)
|
||||
SETUP_BASIC_TEST(type_info entt/core/type_info.cpp)
|
||||
SETUP_BASIC_TEST(type_traits entt/core/type_traits.cpp)
|
||||
SETUP_BASIC_TEST(utility entt/core/utility.cpp)
|
||||
|
||||
# Test entity
|
||||
|
||||
SETUP_AND_ADD_TEST(actor entt/entity/actor.cpp)
|
||||
SETUP_AND_ADD_TEST(entity entt/entity/entity.cpp)
|
||||
SETUP_AND_ADD_TEST(group entt/entity/group.cpp)
|
||||
SETUP_AND_ADD_TEST(helper entt/entity/helper.cpp)
|
||||
SETUP_AND_ADD_TEST(observer entt/entity/observer.cpp)
|
||||
SETUP_AND_ADD_TEST(registry entt/entity/registry.cpp)
|
||||
SETUP_AND_ADD_TEST(runtime_view entt/entity/runtime_view.cpp)
|
||||
SETUP_AND_ADD_TEST(snapshot entt/entity/snapshot.cpp)
|
||||
SETUP_AND_ADD_TEST(sparse_set entt/entity/sparse_set.cpp)
|
||||
SETUP_AND_ADD_TEST(storage entt/entity/storage.cpp)
|
||||
SETUP_AND_ADD_TEST(view entt/entity/view.cpp)
|
||||
SETUP_BASIC_TEST(actor entt/entity/actor.cpp)
|
||||
SETUP_BASIC_TEST(entity entt/entity/entity.cpp)
|
||||
SETUP_BASIC_TEST(group entt/entity/group.cpp)
|
||||
SETUP_BASIC_TEST(handle entt/entity/handle.cpp)
|
||||
SETUP_BASIC_TEST(helper entt/entity/helper.cpp)
|
||||
SETUP_BASIC_TEST(observer entt/entity/observer.cpp)
|
||||
SETUP_BASIC_TEST(registry entt/entity/registry.cpp)
|
||||
SETUP_BASIC_TEST(registry_no_eto entt/entity/registry_no_eto.cpp ENTT_NO_ETO)
|
||||
SETUP_BASIC_TEST(runtime_view entt/entity/runtime_view.cpp)
|
||||
SETUP_BASIC_TEST(snapshot entt/entity/snapshot.cpp)
|
||||
SETUP_BASIC_TEST(sparse_set entt/entity/sparse_set.cpp)
|
||||
SETUP_BASIC_TEST(storage entt/entity/storage.cpp)
|
||||
SETUP_BASIC_TEST(view entt/entity/view.cpp)
|
||||
|
||||
# Test locator
|
||||
|
||||
SETUP_AND_ADD_TEST(locator entt/locator/locator.cpp)
|
||||
SETUP_BASIC_TEST(locator entt/locator/locator.cpp)
|
||||
|
||||
# Test meta
|
||||
|
||||
SETUP_AND_ADD_TEST(meta entt/meta/meta.cpp)
|
||||
SETUP_BASIC_TEST(meta_any entt/meta/meta_any.cpp)
|
||||
SETUP_BASIC_TEST(meta_base entt/meta/meta_base.cpp)
|
||||
SETUP_BASIC_TEST(meta_container entt/meta/meta_container.cpp)
|
||||
SETUP_BASIC_TEST(meta_conv entt/meta/meta_conv.cpp)
|
||||
SETUP_BASIC_TEST(meta_ctor entt/meta/meta_ctor.cpp)
|
||||
SETUP_BASIC_TEST(meta_data entt/meta/meta_data.cpp)
|
||||
SETUP_BASIC_TEST(meta_func entt/meta/meta_func.cpp)
|
||||
SETUP_BASIC_TEST(meta_pointer entt/meta/meta_pointer.cpp)
|
||||
SETUP_BASIC_TEST(meta_prop entt/meta/meta_prop.cpp)
|
||||
SETUP_BASIC_TEST(meta_range entt/meta/meta_range.cpp)
|
||||
SETUP_BASIC_TEST(meta_type entt/meta/meta_type.cpp)
|
||||
|
||||
# Test process
|
||||
|
||||
SETUP_AND_ADD_TEST(process entt/process/process.cpp)
|
||||
SETUP_AND_ADD_TEST(scheduler entt/process/scheduler.cpp)
|
||||
SETUP_BASIC_TEST(process entt/process/process.cpp)
|
||||
SETUP_BASIC_TEST(scheduler entt/process/scheduler.cpp)
|
||||
|
||||
# Test resource
|
||||
|
||||
SETUP_AND_ADD_TEST(resource entt/resource/resource.cpp)
|
||||
SETUP_BASIC_TEST(resource entt/resource/resource.cpp)
|
||||
|
||||
# Test signal
|
||||
|
||||
SETUP_AND_ADD_TEST(delegate entt/signal/delegate.cpp)
|
||||
SETUP_AND_ADD_TEST(dispatcher entt/signal/dispatcher.cpp)
|
||||
SETUP_AND_ADD_TEST(emitter entt/signal/emitter.cpp)
|
||||
SETUP_AND_ADD_TEST(sigh entt/signal/sigh.cpp)
|
||||
SETUP_BASIC_TEST(delegate entt/signal/delegate.cpp)
|
||||
SETUP_BASIC_TEST(dispatcher entt/signal/dispatcher.cpp)
|
||||
SETUP_BASIC_TEST(emitter entt/signal/emitter.cpp)
|
||||
SETUP_BASIC_TEST(sigh entt/signal/sigh.cpp)
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
load("//bazel:copts.bzl", "entt_copts")
|
||||
|
||||
cc_binary(
|
||||
name = "benchmark",
|
||||
srcs = ["benchmark.cpp"],
|
||||
copts = entt_copts,
|
||||
deps = [
|
||||
"//:entt",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <chrono>
|
||||
#include <iterator>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/core/type_info.hpp>
|
||||
#include <entt/entity/registry.hpp>
|
||||
|
||||
struct position {
|
||||
@@ -37,24 +38,24 @@ void pathological(Func func) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 500000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
}
|
||||
|
||||
for(auto i = 0; i < 10; ++i) {
|
||||
registry.each([i = 0, ®istry](const auto entity) mutable {
|
||||
if(!(++i % 7)) { registry.reset<position>(entity); }
|
||||
if(!(++i % 11)) { registry.reset<velocity>(entity); }
|
||||
if(!(++i % 13)) { registry.reset<comp<0>>(entity); }
|
||||
if(!(++i % 7)) { registry.remove_if_exists<position>(entity); }
|
||||
if(!(++i % 11)) { registry.remove_if_exists<velocity>(entity); }
|
||||
if(!(++i % 13)) { registry.remove_if_exists<comp<0>>(entity); }
|
||||
if(!(++i % 17)) { registry.destroy(entity); }
|
||||
});
|
||||
|
||||
for(std::uint64_t j = 0; j < 50000L; j++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,10 +64,10 @@ void pathological(Func func) {
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, Construct) {
|
||||
TEST(Benchmark, Create) {
|
||||
entt::registry registry;
|
||||
|
||||
std::cout << "Constructing 1000000 entities" << std::endl;
|
||||
std::cout << "Creating 1000000 entities" << std::endl;
|
||||
|
||||
timer timer;
|
||||
|
||||
@@ -77,43 +78,45 @@ TEST(Benchmark, Construct) {
|
||||
timer.elapsed();
|
||||
}
|
||||
|
||||
TEST(Benchmark, ConstructMany) {
|
||||
TEST(Benchmark, CreateMany) {
|
||||
entt::registry registry;
|
||||
std::vector<entt::entity> entities(1000000);
|
||||
|
||||
std::cout << "Constructing 1000000 entities at once" << std::endl;
|
||||
std::cout << "Creating 1000000 entities at once" << std::endl;
|
||||
|
||||
timer timer;
|
||||
registry.create(entities.begin(), entities.end());
|
||||
timer.elapsed();
|
||||
}
|
||||
|
||||
TEST(Benchmark, ConstructManyAndAssignComponents) {
|
||||
TEST(Benchmark, CreateManyAndEmplaceComponents) {
|
||||
entt::registry registry;
|
||||
std::vector<entt::entity> entities(1000000);
|
||||
|
||||
std::cout << "Constructing 1000000 entities at once and assign components" << std::endl;
|
||||
std::cout << "Creating 1000000 entities at once and emplace components" << std::endl;
|
||||
|
||||
timer timer;
|
||||
|
||||
registry.create(entities.begin(), entities.end());
|
||||
|
||||
for(const auto entity: entities) {
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
}
|
||||
|
||||
timer.elapsed();
|
||||
}
|
||||
|
||||
TEST(Benchmark, ConstructManyWithComponents) {
|
||||
TEST(Benchmark, CreateManyWithComponents) {
|
||||
entt::registry registry;
|
||||
std::vector<entt::entity> entities(1000000);
|
||||
|
||||
std::cout << "Constructing 1000000 entities at once with components" << std::endl;
|
||||
std::cout << "Creating 1000000 entities at once with components" << std::endl;
|
||||
|
||||
timer timer;
|
||||
registry.create<position, velocity>(entities.begin(), entities.end());
|
||||
registry.create(entities.begin(), entities.end());
|
||||
registry.insert<position>(entities.begin(), entities.end());
|
||||
registry.insert<velocity>(entities.begin(), entities.end());
|
||||
timer.elapsed();
|
||||
}
|
||||
|
||||
@@ -142,10 +145,10 @@ TEST(Benchmark, IterateSingleComponent1M) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.view<position>().each(func);
|
||||
timer.elapsed();
|
||||
@@ -163,11 +166,11 @@ TEST(Benchmark, IterateSingleComponentRuntime1M) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
entt::component types[] = { registry.type<position>() };
|
||||
auto test = [&](auto func) {
|
||||
entt::id_type types[] = { entt::type_info<position>::id() };
|
||||
|
||||
timer timer;
|
||||
registry.runtime_view(std::begin(types), std::end(types)).each(func);
|
||||
@@ -186,11 +189,11 @@ TEST(Benchmark, IterateTwoComponents1M) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.view<position, velocity>().each(func);
|
||||
timer.elapsed();
|
||||
@@ -208,14 +211,14 @@ TEST(Benchmark, IterateTwoComponents1MHalf) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<velocity>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
|
||||
if(i % 2) {
|
||||
registry.assign<position>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.view<position, velocity>().each(func);
|
||||
timer.elapsed();
|
||||
@@ -233,14 +236,14 @@ TEST(Benchmark, IterateTwoComponents1MOne) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<velocity>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
|
||||
if(i == 500000L) {
|
||||
registry.assign<position>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.view<position, velocity>().each(func);
|
||||
timer.elapsed();
|
||||
@@ -253,19 +256,19 @@ TEST(Benchmark, IterateTwoComponents1MOne) {
|
||||
|
||||
TEST(Benchmark, IterateTwoComponentsNonOwningGroup1M) {
|
||||
entt::registry registry;
|
||||
registry.group<>(entt::get<position, velocity>);
|
||||
const auto group = registry.group<>(entt::get<position, velocity>);
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, two components, non owning group" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.group<>(entt::get<position, velocity>).each(func);
|
||||
group.each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
@@ -276,19 +279,19 @@ TEST(Benchmark, IterateTwoComponentsNonOwningGroup1M) {
|
||||
|
||||
TEST(Benchmark, IterateTwoComponentsFullOwningGroup1M) {
|
||||
entt::registry registry;
|
||||
registry.group<position, velocity>();
|
||||
const auto group = registry.group<position, velocity>();
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, two components, full owning group" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.group<position, velocity>().each(func);
|
||||
group.each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
@@ -299,19 +302,19 @@ TEST(Benchmark, IterateTwoComponentsFullOwningGroup1M) {
|
||||
|
||||
TEST(Benchmark, IterateTwoComponentsPartialOwningGroup1M) {
|
||||
entt::registry registry;
|
||||
registry.group<position>(entt::get<velocity>);
|
||||
const auto group = registry.group<position>(entt::get<velocity>);
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, two components, partial owning group" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.group<position>(entt::get<velocity>).each(func);
|
||||
group.each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
@@ -327,12 +330,15 @@ TEST(Benchmark, IterateTwoComponentsRuntime1M) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
entt::component types[] = { registry.type<position>(), registry.type<velocity>() };
|
||||
auto test = [&](auto func) {
|
||||
entt::id_type types[] = {
|
||||
entt::type_info<position>::id(),
|
||||
entt::type_info<velocity>::id()
|
||||
};
|
||||
|
||||
timer timer;
|
||||
registry.runtime_view(std::begin(types), std::end(types)).each(func);
|
||||
@@ -352,15 +358,18 @@ TEST(Benchmark, IterateTwoComponentsRuntime1MHalf) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<velocity>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
|
||||
if(i % 2) {
|
||||
registry.assign<position>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
entt::component types[] = { registry.type<position>(), registry.type<velocity>() };
|
||||
auto test = [&](auto func) {
|
||||
entt::id_type types[] = {
|
||||
entt::type_info<position>::id(),
|
||||
entt::type_info<velocity>::id()
|
||||
};
|
||||
|
||||
timer timer;
|
||||
registry.runtime_view(std::begin(types), std::end(types)).each(func);
|
||||
@@ -380,15 +389,18 @@ TEST(Benchmark, IterateTwoComponentsRuntime1MOne) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<velocity>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
|
||||
if(i == 500000L) {
|
||||
registry.assign<position>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
entt::component types[] = { registry.type<position>(), registry.type<velocity>() };
|
||||
auto test = [&](auto func) {
|
||||
entt::id_type types[] = {
|
||||
entt::type_info<position>::id(),
|
||||
entt::type_info<velocity>::id()
|
||||
};
|
||||
|
||||
timer timer;
|
||||
registry.runtime_view(std::begin(types), std::end(types)).each(func);
|
||||
@@ -408,12 +420,12 @@ TEST(Benchmark, IterateThreeComponents1M) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.view<position, velocity, comp<0>>().each(func);
|
||||
timer.elapsed();
|
||||
@@ -431,15 +443,15 @@ TEST(Benchmark, IterateThreeComponents1MHalf) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
|
||||
if(i % 2) {
|
||||
registry.assign<position>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.view<position, velocity, comp<0>>().each(func);
|
||||
timer.elapsed();
|
||||
@@ -457,15 +469,15 @@ TEST(Benchmark, IterateThreeComponents1MOne) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
|
||||
if(i == 500000L) {
|
||||
registry.assign<position>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.view<position, velocity, comp<0>>().each(func);
|
||||
timer.elapsed();
|
||||
@@ -478,20 +490,20 @@ TEST(Benchmark, IterateThreeComponents1MOne) {
|
||||
|
||||
TEST(Benchmark, IterateThreeComponentsNonOwningGroup1M) {
|
||||
entt::registry registry;
|
||||
registry.group<>(entt::get<position, velocity, comp<0>>);
|
||||
const auto group = registry.group<>(entt::get<position, velocity, comp<0>>);
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, three components, non owning group" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.group<>(entt::get<position, velocity, comp<0>>).each(func);
|
||||
group.each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
@@ -502,20 +514,20 @@ TEST(Benchmark, IterateThreeComponentsNonOwningGroup1M) {
|
||||
|
||||
TEST(Benchmark, IterateThreeComponentsFullOwningGroup1M) {
|
||||
entt::registry registry;
|
||||
registry.group<position, velocity, comp<0>>();
|
||||
const auto group = registry.group<position, velocity, comp<0>>();
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, three components, full owning group" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.group<position, velocity, comp<0>>().each(func);
|
||||
group.each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
@@ -526,20 +538,20 @@ TEST(Benchmark, IterateThreeComponentsFullOwningGroup1M) {
|
||||
|
||||
TEST(Benchmark, IterateThreeComponentsPartialOwningGroup1M) {
|
||||
entt::registry registry;
|
||||
registry.group<position, velocity>(entt::get<comp<0>>);
|
||||
const auto group = registry.group<position, velocity>(entt::get<comp<0>>);
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, three components, partial owning group" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.group<position, velocity>(entt::get<comp<0>>).each(func);
|
||||
group.each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
@@ -555,13 +567,17 @@ TEST(Benchmark, IterateThreeComponentsRuntime1M) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
entt::component types[] = { registry.type<position>(), registry.type<velocity>(), registry.type<comp<0>>() };
|
||||
auto test = [&](auto func) {
|
||||
entt::id_type types[] = {
|
||||
entt::type_info<position>::id(),
|
||||
entt::type_info<velocity>::id(),
|
||||
entt::type_info<comp<0>>::id()
|
||||
};
|
||||
|
||||
timer timer;
|
||||
registry.runtime_view(std::begin(types), std::end(types)).each(func);
|
||||
@@ -582,16 +598,20 @@ TEST(Benchmark, IterateThreeComponentsRuntime1MHalf) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
|
||||
if(i % 2) {
|
||||
registry.assign<position>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
entt::component types[] = { registry.type<position>(), registry.type<velocity>(), registry.type<comp<0>>() };
|
||||
auto test = [&](auto func) {
|
||||
entt::id_type types[] = {
|
||||
entt::type_info<position>::id(),
|
||||
entt::type_info<velocity>::id(),
|
||||
entt::type_info<comp<0>>::id()
|
||||
};
|
||||
|
||||
timer timer;
|
||||
registry.runtime_view(std::begin(types), std::end(types)).each(func);
|
||||
@@ -612,16 +632,20 @@ TEST(Benchmark, IterateThreeComponentsRuntime1MOne) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
|
||||
if(i == 500000L) {
|
||||
registry.assign<position>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
entt::component types[] = { registry.type<position>(), registry.type<velocity>(), registry.type<comp<0>>() };
|
||||
auto test = [&](auto func) {
|
||||
entt::id_type types[] = {
|
||||
entt::type_info<position>::id(),
|
||||
entt::type_info<velocity>::id(),
|
||||
entt::type_info<comp<0>>::id()
|
||||
};
|
||||
|
||||
timer timer;
|
||||
registry.runtime_view(std::begin(types), std::end(types)).each(func);
|
||||
@@ -642,14 +666,14 @@ TEST(Benchmark, IterateFiveComponents1M) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.assign<comp<1>>(entity);
|
||||
registry.assign<comp<2>>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
registry.emplace<comp<1>>(entity);
|
||||
registry.emplace<comp<2>>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.view<position, velocity, comp<0>, comp<1>, comp<2>>().each(func);
|
||||
timer.elapsed();
|
||||
@@ -667,17 +691,17 @@ TEST(Benchmark, IterateFiveComponents1MHalf) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.assign<comp<1>>(entity);
|
||||
registry.assign<comp<2>>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
registry.emplace<comp<1>>(entity);
|
||||
registry.emplace<comp<2>>(entity);
|
||||
|
||||
if(i % 2) {
|
||||
registry.assign<position>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.view<position, velocity, comp<0>, comp<1>, comp<2>>().each(func);
|
||||
timer.elapsed();
|
||||
@@ -695,17 +719,17 @@ TEST(Benchmark, IterateFiveComponents1MOne) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.assign<comp<1>>(entity);
|
||||
registry.assign<comp<2>>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
registry.emplace<comp<1>>(entity);
|
||||
registry.emplace<comp<2>>(entity);
|
||||
|
||||
if(i == 500000L) {
|
||||
registry.assign<position>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.view<position, velocity, comp<0>, comp<1>, comp<2>>().each(func);
|
||||
timer.elapsed();
|
||||
@@ -718,22 +742,22 @@ TEST(Benchmark, IterateFiveComponents1MOne) {
|
||||
|
||||
TEST(Benchmark, IterateFiveComponentsNonOwningGroup1M) {
|
||||
entt::registry registry;
|
||||
registry.group<>(entt::get<position, velocity, comp<0>, comp<1>, comp<2>>);
|
||||
const auto group = registry.group<>(entt::get<position, velocity, comp<0>, comp<1>, comp<2>>);
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, five components, non owning group" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.assign<comp<1>>(entity);
|
||||
registry.assign<comp<2>>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
registry.emplace<comp<1>>(entity);
|
||||
registry.emplace<comp<2>>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.group<>(entt::get<position, velocity, comp<0>, comp<1>, comp<2>>).each(func);
|
||||
group.each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
@@ -744,22 +768,22 @@ TEST(Benchmark, IterateFiveComponentsNonOwningGroup1M) {
|
||||
|
||||
TEST(Benchmark, IterateFiveComponentsFullOwningGroup1M) {
|
||||
entt::registry registry;
|
||||
registry.group<position, velocity, comp<0>, comp<1>, comp<2>>();
|
||||
const auto group = registry.group<position, velocity, comp<0>, comp<1>, comp<2>>();
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, five components, full owning group" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.assign<comp<1>>(entity);
|
||||
registry.assign<comp<2>>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
registry.emplace<comp<1>>(entity);
|
||||
registry.emplace<comp<2>>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.group<position, velocity, comp<0>, comp<1>, comp<2>>().each(func);
|
||||
group.each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
@@ -770,22 +794,22 @@ TEST(Benchmark, IterateFiveComponentsFullOwningGroup1M) {
|
||||
|
||||
TEST(Benchmark, IterateFiveComponentsPartialFourOfFiveOwningGroup1M) {
|
||||
entt::registry registry;
|
||||
registry.group<position, velocity, comp<0>, comp<1>>(entt::get<comp<2>>);
|
||||
const auto group = registry.group<position, velocity, comp<0>, comp<1>>(entt::get<comp<2>>);
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, five components, partial (4 of 5) owning group" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.assign<comp<1>>(entity);
|
||||
registry.assign<comp<2>>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
registry.emplace<comp<1>>(entity);
|
||||
registry.emplace<comp<2>>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.group<position, velocity, comp<0>, comp<1>>(entt::get<comp<2>>).each(func);
|
||||
group.each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
@@ -796,22 +820,22 @@ TEST(Benchmark, IterateFiveComponentsPartialFourOfFiveOwningGroup1M) {
|
||||
|
||||
TEST(Benchmark, IterateFiveComponentsPartialThreeOfFiveOwningGroup1M) {
|
||||
entt::registry registry;
|
||||
registry.group<position, velocity, comp<0>>(entt::get<comp<1>, comp<2>>);
|
||||
const auto group = registry.group<position, velocity, comp<0>>(entt::get<comp<1>, comp<2>>);
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, five components, partial (3 of 5) owning group" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.assign<comp<1>>(entity);
|
||||
registry.assign<comp<2>>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
registry.emplace<comp<1>>(entity);
|
||||
registry.emplace<comp<2>>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
auto test = [&](auto func) {
|
||||
timer timer;
|
||||
registry.group<position, velocity, comp<0>>(entt::get<comp<1>, comp<2>>).each(func);
|
||||
group.each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
@@ -827,20 +851,20 @@ TEST(Benchmark, IterateFiveComponentsRuntime1M) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity);
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.assign<comp<1>>(entity);
|
||||
registry.assign<comp<2>>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
registry.emplace<comp<1>>(entity);
|
||||
registry.emplace<comp<2>>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
entt::component types[] = {
|
||||
registry.type<position>(),
|
||||
registry.type<velocity>(),
|
||||
registry.type<comp<0>>(),
|
||||
registry.type<comp<1>>(),
|
||||
registry.type<comp<2>>()
|
||||
auto test = [&](auto func) {
|
||||
entt::id_type types[] = {
|
||||
entt::type_info<position>::id(),
|
||||
entt::type_info<velocity>::id(),
|
||||
entt::type_info<comp<0>>::id(),
|
||||
entt::type_info<comp<1>>::id(),
|
||||
entt::type_info<comp<2>>::id()
|
||||
};
|
||||
|
||||
timer timer;
|
||||
@@ -864,23 +888,23 @@ TEST(Benchmark, IterateFiveComponentsRuntime1MHalf) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.assign<comp<1>>(entity);
|
||||
registry.assign<comp<2>>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
registry.emplace<comp<1>>(entity);
|
||||
registry.emplace<comp<2>>(entity);
|
||||
|
||||
if(i % 2) {
|
||||
registry.assign<position>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
entt::component types[] = {
|
||||
registry.type<position>(),
|
||||
registry.type<velocity>(),
|
||||
registry.type<comp<0>>(),
|
||||
registry.type<comp<1>>(),
|
||||
registry.type<comp<2>>()
|
||||
auto test = [&](auto func) {
|
||||
entt::id_type types[] = {
|
||||
entt::type_info<position>::id(),
|
||||
entt::type_info<velocity>::id(),
|
||||
entt::type_info<comp<0>>::id(),
|
||||
entt::type_info<comp<1>>::id(),
|
||||
entt::type_info<comp<2>>::id()
|
||||
};
|
||||
|
||||
timer timer;
|
||||
@@ -904,23 +928,23 @@ TEST(Benchmark, IterateFiveComponentsRuntime1MOne) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<velocity>(entity);
|
||||
registry.assign<comp<0>>(entity);
|
||||
registry.assign<comp<1>>(entity);
|
||||
registry.assign<comp<2>>(entity);
|
||||
registry.emplace<velocity>(entity);
|
||||
registry.emplace<comp<0>>(entity);
|
||||
registry.emplace<comp<1>>(entity);
|
||||
registry.emplace<comp<2>>(entity);
|
||||
|
||||
if(i == 500000L) {
|
||||
registry.assign<position>(entity);
|
||||
registry.emplace<position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
entt::component types[] = {
|
||||
registry.type<position>(),
|
||||
registry.type<velocity>(),
|
||||
registry.type<comp<0>>(),
|
||||
registry.type<comp<1>>(),
|
||||
registry.type<comp<2>>()
|
||||
auto test = [&](auto func) {
|
||||
entt::id_type types[] = {
|
||||
entt::type_info<position>::id(),
|
||||
entt::type_info<velocity>::id(),
|
||||
entt::type_info<comp<0>>::id(),
|
||||
entt::type_info<comp<1>>::id(),
|
||||
entt::type_info<comp<2>>::id()
|
||||
};
|
||||
|
||||
timer timer;
|
||||
@@ -990,7 +1014,7 @@ TEST(Benchmark, SortSingle) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 150000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity, i, i);
|
||||
registry.emplace<position>(entity, i, i);
|
||||
}
|
||||
|
||||
timer timer;
|
||||
@@ -1009,8 +1033,8 @@ TEST(Benchmark, SortMulti) {
|
||||
|
||||
for(std::uint64_t i = 0; i < 150000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity, i, i);
|
||||
registry.assign<velocity>(entity, i, i);
|
||||
registry.emplace<position>(entity, i, i);
|
||||
registry.emplace<velocity>(entity, i, i);
|
||||
}
|
||||
|
||||
registry.sort<position>([](const auto &lhs, const auto &rhs) {
|
||||
@@ -1026,13 +1050,13 @@ TEST(Benchmark, SortMulti) {
|
||||
|
||||
TEST(Benchmark, AlmostSortedStdSort) {
|
||||
entt::registry registry;
|
||||
entt::entity entities[3];
|
||||
entt::entity entities[3]{};
|
||||
|
||||
std::cout << "Sort 150000 entities, almost sorted, std::sort" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 150000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity, i, i);
|
||||
registry.emplace<position>(entity, i, i);
|
||||
|
||||
if(!(i % 50000)) {
|
||||
entities[i / 50000] = entity;
|
||||
@@ -1042,7 +1066,7 @@ TEST(Benchmark, AlmostSortedStdSort) {
|
||||
for(std::uint64_t i = 0; i < 3; ++i) {
|
||||
registry.destroy(entities[i]);
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity, 50000 * i, 50000 * i);
|
||||
registry.emplace<position>(entity, 50000 * i, 50000 * i);
|
||||
}
|
||||
|
||||
timer timer;
|
||||
@@ -1056,13 +1080,13 @@ TEST(Benchmark, AlmostSortedStdSort) {
|
||||
|
||||
TEST(Benchmark, AlmostSortedInsertionSort) {
|
||||
entt::registry registry;
|
||||
entt::entity entities[3];
|
||||
entt::entity entities[3]{};
|
||||
|
||||
std::cout << "Sort 150000 entities, almost sorted, insertion sort" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 150000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity, i, i);
|
||||
registry.emplace<position>(entity, i, i);
|
||||
|
||||
if(!(i % 50000)) {
|
||||
entities[i / 50000] = entity;
|
||||
@@ -1072,7 +1096,7 @@ TEST(Benchmark, AlmostSortedInsertionSort) {
|
||||
for(std::uint64_t i = 0; i < 3; ++i) {
|
||||
registry.destroy(entities[i]);
|
||||
const auto entity = registry.create();
|
||||
registry.assign<position>(entity, 50000 * i, 50000 * i);
|
||||
registry.emplace<position>(entity, 50000 * i, 50000 * i);
|
||||
}
|
||||
|
||||
timer timer;
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
load("//bazel:copts.bzl", "entt_copts")
|
||||
|
||||
cc_test(
|
||||
name = "algorithm",
|
||||
srcs = ["algorithm.cpp"],
|
||||
copts = entt_copts,
|
||||
deps = [
|
||||
"//:entt",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "family",
|
||||
srcs = ["family.cpp"],
|
||||
copts = entt_copts,
|
||||
deps = [
|
||||
"//:entt",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "hashed_string",
|
||||
srcs = ["hashed_string.cpp"],
|
||||
copts = entt_copts,
|
||||
deps = [
|
||||
"//:entt",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "ident",
|
||||
srcs = ["ident.cpp"],
|
||||
copts = entt_copts,
|
||||
deps = [
|
||||
"//:entt",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "monostate",
|
||||
srcs = ["monostate.cpp"],
|
||||
copts = entt_copts,
|
||||
deps = [
|
||||
"//:entt",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "type_traits",
|
||||
srcs = ["type_traits.cpp"],
|
||||
copts = entt_copts,
|
||||
deps = [
|
||||
"//:entt",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "utility",
|
||||
srcs = ["utility.cpp"],
|
||||
copts = entt_copts,
|
||||
deps = [
|
||||
"//:entt",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
@@ -16,7 +16,7 @@ TEST(Family, Functionalities) {
|
||||
}
|
||||
|
||||
TEST(Family, Uniqueness) {
|
||||
ASSERT_EQ(a_family::type<int>, a_family::type<int &>);
|
||||
ASSERT_EQ(a_family::type<int>, a_family::type<int &&>);
|
||||
ASSERT_EQ(a_family::type<int>, a_family::type<const int &>);
|
||||
ASSERT_NE(a_family::type<int>, a_family::type<int &>);
|
||||
ASSERT_NE(a_family::type<int>, a_family::type<int &&>);
|
||||
ASSERT_NE(a_family::type<int>, a_family::type<const int &>);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user