Compare commits
2085 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e07b981f0 | ||
|
|
756e909f5e | ||
|
|
2404392daf | ||
|
|
bf6576f5af | ||
|
|
158c2ab76e | ||
|
|
56fa50385e | ||
|
|
eb05ab5fab | ||
|
|
7f87b637d5 | ||
|
|
4b213adc75 | ||
|
|
7e7d5bbf17 | ||
|
|
e18c0a0c19 | ||
|
|
02b6ffc771 | ||
|
|
340d66c24c | ||
|
|
2cbf9d3620 | ||
|
|
11e6793544 | ||
|
|
d07e0336a2 | ||
|
|
7dbfb3d9ae | ||
|
|
02ef5db6e3 | ||
|
|
bb43bb5508 | ||
|
|
a771f083a7 | ||
|
|
67f8bce1dd | ||
|
|
3b6dd23598 | ||
|
|
66bbeae7da | ||
|
|
059334a861 | ||
|
|
f874c8309f | ||
|
|
27be812cc3 | ||
|
|
2adc2e97e1 | ||
|
|
dc4279a2d0 | ||
|
|
85bff4525a | ||
|
|
bd648c0745 | ||
|
|
d5e7005edd | ||
|
|
8913c7ea18 | ||
|
|
0e2afe4f98 | ||
|
|
a936f7cdbe | ||
|
|
44fa466618 | ||
|
|
b05d84e8e3 | ||
|
|
7787c1ddd7 | ||
|
|
52bfddd2e7 | ||
|
|
f59adcdc37 | ||
|
|
e996d3398f | ||
|
|
d73892d25a | ||
|
|
1ebf614d79 | ||
|
|
f9c995f03f | ||
|
|
2dcdd561f0 | ||
|
|
90f97aa8d7 | ||
|
|
5a4b067cee | ||
|
|
cabcc761c6 | ||
|
|
a40ac1c46c | ||
|
|
85ddfc4d21 | ||
|
|
037a35df1d | ||
|
|
64c753023e | ||
|
|
9d72ffb9fe | ||
|
|
4162d4fbc6 | ||
|
|
a4d16bffd3 | ||
|
|
bcaf1489c0 | ||
|
|
a85712e8aa | ||
|
|
0f95d02cdc | ||
|
|
c151d55237 | ||
|
|
785cd6bc11 | ||
|
|
8ec7e3cc97 | ||
|
|
e376493970 | ||
|
|
08e2322d79 | ||
|
|
2832767daa | ||
|
|
62e12ee0aa | ||
|
|
80e73c1089 | ||
|
|
4f93995bd7 | ||
|
|
03c7dac92f | ||
|
|
d0b93f565a | ||
|
|
1512fbae55 | ||
|
|
4d91afdf9f | ||
|
|
c239c3fea3 | ||
|
|
a0b431e8c4 | ||
|
|
005e03aeb3 | ||
|
|
6002d373e2 | ||
|
|
a53066424b | ||
|
|
38ab02ff88 | ||
|
|
fee0b29a0b | ||
|
|
2553695029 | ||
|
|
301939983f | ||
|
|
cd9ae1fbad | ||
|
|
54e9bc86cc | ||
|
|
a1dd4c28c3 | ||
|
|
6d3857f337 | ||
|
|
c4be3e731a | ||
|
|
ea4407b847 | ||
|
|
a62a83044f | ||
|
|
e7e7b06744 | ||
|
|
2222e31885 | ||
|
|
c3f7f83c55 | ||
|
|
86e18f68c4 | ||
|
|
cc7d9e03d9 | ||
|
|
c8639ae434 | ||
|
|
85cf32516e | ||
|
|
bc85817b07 | ||
|
|
1016394be5 | ||
|
|
de35cbf4af | ||
|
|
bb1acee36a | ||
|
|
c6a2ed78c3 | ||
|
|
7729958082 | ||
|
|
ccdaec86b3 | ||
|
|
42afeef993 | ||
|
|
3d06911886 | ||
|
|
98f929e41e | ||
|
|
94d4e0231e | ||
|
|
e60cdb2e3b | ||
|
|
550c021097 | ||
|
|
1aec7f71b8 | ||
|
|
07565927f2 | ||
|
|
fda78ede1b | ||
|
|
1249a4f8d3 | ||
|
|
4e631f1536 | ||
|
|
1757dbc225 | ||
|
|
8149e204d2 | ||
|
|
412f2ef63e | ||
|
|
ea33673daa | ||
|
|
169b816613 | ||
|
|
48edf077fc | ||
|
|
91db153710 | ||
|
|
ea1f010b1a | ||
|
|
935a852745 | ||
|
|
0eb6d7de3c | ||
|
|
3118cd4fa5 | ||
|
|
18832fcb37 | ||
|
|
612554f6af | ||
|
|
1c794591b9 | ||
|
|
a8d95b284d | ||
|
|
772c2dba2f | ||
|
|
9d1a210c97 | ||
|
|
b04b966db1 | ||
|
|
e2c61d90e9 | ||
|
|
d50655c4f7 | ||
|
|
5e4c63736b | ||
|
|
b90b71c8c0 | ||
|
|
722033e906 | ||
|
|
b6ec91fcd0 | ||
|
|
e94c0d003a | ||
|
|
5c46ccb37e | ||
|
|
fd989feea3 | ||
|
|
1b53e83dde | ||
|
|
3ebe827b51 | ||
|
|
b8266e1169 | ||
|
|
4efbe24607 | ||
|
|
e96ac1f6ff | ||
|
|
14915368c7 | ||
|
|
0ff5c18743 | ||
|
|
240814cc85 | ||
|
|
1912350cc6 | ||
|
|
146faa6008 | ||
|
|
e5fa67850b | ||
|
|
1377b51341 | ||
|
|
db67fb3539 | ||
|
|
6d216406f3 | ||
|
|
ab225eaec1 | ||
|
|
4d6d9e567d | ||
|
|
0fcf0142ba | ||
|
|
ab907e4fef | ||
|
|
1c1c3eb271 | ||
|
|
a03a569534 | ||
|
|
309fb0fa83 | ||
|
|
14c1431848 | ||
|
|
c3dc32415b | ||
|
|
44d6246278 | ||
|
|
5bbd7e03f9 | ||
|
|
f5f463b411 | ||
|
|
6eaefbe25c | ||
|
|
4b5c2c85a5 | ||
|
|
0f951cd322 | ||
|
|
cf0da32fd0 | ||
|
|
5a3085c42b | ||
|
|
42cc480bf5 | ||
|
|
2822eda858 | ||
|
|
6c804b5ca2 | ||
|
|
5149d1395e | ||
|
|
f75a2dab8d | ||
|
|
a045c88c61 | ||
|
|
a5fdf917df | ||
|
|
80a0e47f1a | ||
|
|
a304313ad4 | ||
|
|
05b1d5a4da | ||
|
|
edeef3541c | ||
|
|
cd929d8e65 | ||
|
|
b005971427 | ||
|
|
c35614fb63 | ||
|
|
18e16b09f2 | ||
|
|
b18b76c1f0 | ||
|
|
c9f65267ba | ||
|
|
cce52c673b | ||
|
|
fdde216f0e | ||
|
|
d1901a97d1 | ||
|
|
eb2077b95b | ||
|
|
fad3bdeed4 | ||
|
|
e1a537e547 | ||
|
|
ba098e1199 | ||
|
|
0ec755fccf | ||
|
|
997dd433b3 | ||
|
|
4aea1567db | ||
|
|
446c8df3b4 | ||
|
|
f4bd868d6a | ||
|
|
8490264af3 | ||
|
|
505cfdd193 | ||
|
|
39ecd1545c | ||
|
|
9b80c0028d | ||
|
|
35276b55af | ||
|
|
eb63dd6227 | ||
|
|
9f24b3c584 | ||
|
|
53e951d96d | ||
|
|
17dd386937 | ||
|
|
3eb00382e7 | ||
|
|
98b5f7a26a | ||
|
|
fb70ec1cb0 | ||
|
|
47ada87ba2 | ||
|
|
7a949dd328 | ||
|
|
3982217b67 | ||
|
|
8cb61da9c2 | ||
|
|
375c780d3b | ||
|
|
f3b6c2ce5d | ||
|
|
a7a2c96382 | ||
|
|
99336ef746 | ||
|
|
72d2adfe1c | ||
|
|
707574e294 | ||
|
|
18ffbd0027 | ||
|
|
68a0e4f39a | ||
|
|
ddb99ced34 | ||
|
|
cb2782bc71 | ||
|
|
52cade9267 | ||
|
|
ec600eb2f4 | ||
|
|
61e96e2dab | ||
|
|
9f5433053d | ||
|
|
d2489a57b7 | ||
|
|
e5841cd467 | ||
|
|
820d52428f | ||
|
|
ae6931b046 | ||
|
|
90a97b90bb | ||
|
|
4b38d81f00 | ||
|
|
8c27ca9e36 | ||
|
|
4b89fc390d | ||
|
|
29c1766714 | ||
|
|
2e5a9f117c | ||
|
|
2abcbcd30f | ||
|
|
ac8bde4ea8 | ||
|
|
78a13efc6c | ||
|
|
c99e859978 | ||
|
|
ed448cb99f | ||
|
|
5d472a1f2e | ||
|
|
92a938db0e | ||
|
|
aa979b2046 | ||
|
|
2daaf899d3 | ||
|
|
fa4c6bccbc | ||
|
|
bedea4b11a | ||
|
|
bc415242f2 | ||
|
|
2eeb7552d6 | ||
|
|
3a6ecb10a7 | ||
|
|
f55004d1e4 | ||
|
|
1a3304a886 | ||
|
|
58bcbe75b7 | ||
|
|
753df6decf | ||
|
|
c32f947c18 | ||
|
|
6123e4ac8a | ||
|
|
c52f16f5b8 | ||
|
|
3dac65a012 | ||
|
|
bc089bc4c1 | ||
|
|
1075ded4dc | ||
|
|
5578ea2f30 | ||
|
|
ade3e58829 | ||
|
|
ee2e88476c | ||
|
|
1726e983ad | ||
|
|
44341f0bcc | ||
|
|
366da3d0fe | ||
|
|
f7fa2f605b | ||
|
|
6854dea47e | ||
|
|
9d7e1cd99d | ||
|
|
330f3dff77 | ||
|
|
073c46e7bf | ||
|
|
9950b2ea34 | ||
|
|
a7c2a5c49b | ||
|
|
c2c39cd0d0 | ||
|
|
b6e7c36b11 | ||
|
|
002f92fe1e | ||
|
|
d6d0cdf9aa | ||
|
|
7d002df94f | ||
|
|
f0d7408f94 | ||
|
|
7154678347 | ||
|
|
68f0743c40 | ||
|
|
3d5905c878 | ||
|
|
a344a1ca59 | ||
|
|
5f539ddfb7 | ||
|
|
a974235d10 | ||
|
|
2d17ef5c7e | ||
|
|
dda8c209df | ||
|
|
be88d5dfb8 | ||
|
|
4a537d82d3 | ||
|
|
6f36723097 | ||
|
|
35bce924f8 | ||
|
|
9356e8ccb5 | ||
|
|
1ffde24ef6 | ||
|
|
2f52bef882 | ||
|
|
b1369582e1 | ||
|
|
cf176719a8 | ||
|
|
d7ed72ac06 | ||
|
|
83322664f1 | ||
|
|
5af7e96f70 | ||
|
|
c95041d7c3 | ||
|
|
62f40dcb21 | ||
|
|
b4f88e303a | ||
|
|
cd0ec69446 | ||
|
|
909415d167 | ||
|
|
337bc6d97a | ||
|
|
6c9fab7da1 | ||
|
|
315b1f5913 | ||
|
|
c2d1859858 | ||
|
|
37ba7316e0 | ||
|
|
fc1292b1d1 | ||
|
|
0507680cc1 | ||
|
|
60475d3354 | ||
|
|
b1cd794b2c | ||
|
|
cd98ecff88 | ||
|
|
f8ec4027ee | ||
|
|
d980a5cf44 | ||
|
|
761604d044 | ||
|
|
0f4161f616 | ||
|
|
4a668c97a3 | ||
|
|
9d687c25b3 | ||
|
|
0b73082aa0 | ||
|
|
085e1ae157 | ||
|
|
7c0db3203b | ||
|
|
bc987ed03f | ||
|
|
57e1bf3afd | ||
|
|
90606bd410 | ||
|
|
9744a3745c | ||
|
|
14e051f970 | ||
|
|
2396c9b6da | ||
|
|
c386a9d140 | ||
|
|
12847593c2 | ||
|
|
6cfa024ea2 | ||
|
|
4b43d99660 | ||
|
|
c1bfc29e7f | ||
|
|
7e91e27f14 | ||
|
|
f8015036a9 | ||
|
|
b7ef1607dc | ||
|
|
b93a31dd5b | ||
|
|
fa09964f10 | ||
|
|
0038728be3 | ||
|
|
ec56651959 | ||
|
|
8259371d6a | ||
|
|
d8623991c0 | ||
|
|
0c205431b0 | ||
|
|
58dbaf4a51 | ||
|
|
3b50ebae83 | ||
|
|
8b895eff7a | ||
|
|
70af0365bf | ||
|
|
bb3e3bfa1f | ||
|
|
54aa98a752 | ||
|
|
3bff0aa1ea | ||
|
|
b012231635 | ||
|
|
6c6f05fc91 | ||
|
|
3338d5e105 | ||
|
|
390ec2f0c1 | ||
|
|
4ebdf7a6e8 | ||
|
|
6f1366dbb5 | ||
|
|
503791d3c0 | ||
|
|
902dc49de2 | ||
|
|
2d299f31e5 | ||
|
|
df298587b8 | ||
|
|
b9c33008d8 | ||
|
|
ca97f1c4a3 | ||
|
|
effed56653 | ||
|
|
7b501aa89b | ||
|
|
6c0d0d465b | ||
|
|
a52bd20912 | ||
|
|
aabcc445ee | ||
|
|
d491e504cc | ||
|
|
3142e430e7 | ||
|
|
b784d80a50 | ||
|
|
7344654308 | ||
|
|
e01b35c39c | ||
|
|
b8b2c4ee73 | ||
|
|
8cd1ce97f7 | ||
|
|
05b514b743 | ||
|
|
22596e0f5b | ||
|
|
e7031794df | ||
|
|
af69a0a1fd | ||
|
|
52fa690764 | ||
|
|
4ad411e320 | ||
|
|
09da190e93 | ||
|
|
797dd56bb7 | ||
|
|
5e1d77fcd5 | ||
|
|
2820f2658b | ||
|
|
f3fc32660b | ||
|
|
2d4b00ec24 | ||
|
|
86808f5035 | ||
|
|
82946e2372 | ||
|
|
22d117a397 | ||
|
|
662cfc9fdc | ||
|
|
a3e73909cf | ||
|
|
95bafc8ced | ||
|
|
05f1906f77 | ||
|
|
dbbb131424 | ||
|
|
fc7ec35242 | ||
|
|
7ac10472dc | ||
|
|
d255889c5c | ||
|
|
7662da22fc | ||
|
|
64cc200df0 | ||
|
|
63c983683c | ||
|
|
92f79c99f7 | ||
|
|
24f937fe4f | ||
|
|
47bb682890 | ||
|
|
9ab674ef9a | ||
|
|
83eb0de916 | ||
|
|
6359a12dbb | ||
|
|
eb8e96f413 | ||
|
|
138bd96167 | ||
|
|
0d5ea4711d | ||
|
|
fa4212f07b | ||
|
|
c7635a7e3d | ||
|
|
3923c4cfd2 | ||
|
|
6ad884080c | ||
|
|
c578a43611 | ||
|
|
aebfa7161f | ||
|
|
682bc502e2 | ||
|
|
cf018ff74c | ||
|
|
5a9cb251ad | ||
|
|
bbbf5a5edf | ||
|
|
7df0449100 | ||
|
|
820add87ea | ||
|
|
9a574aa00c | ||
|
|
10da82a331 | ||
|
|
26c816e78c | ||
|
|
808e238384 | ||
|
|
ff068e032b | ||
|
|
5072e96277 | ||
|
|
c55c1c0615 | ||
|
|
9983faef70 | ||
|
|
70a264e939 | ||
|
|
9ef6008c67 | ||
|
|
5a3e816544 | ||
|
|
3e385fb884 | ||
|
|
fa5cce7477 | ||
|
|
b2de76e8a0 | ||
|
|
edb363b236 | ||
|
|
12dcfdb5e1 | ||
|
|
86c1dd7f32 | ||
|
|
d8ff4285da | ||
|
|
7effde0478 | ||
|
|
559ee931fb | ||
|
|
cb27a7fe3e | ||
|
|
c33a8dc2c8 | ||
|
|
11fcf88b88 | ||
|
|
4f0baa80d7 | ||
|
|
ddea97bc88 | ||
|
|
102c9891a0 | ||
|
|
fb4784b695 | ||
|
|
9a0650c703 | ||
|
|
a62b93acec | ||
|
|
2d11bdc736 | ||
|
|
7d263d36a0 | ||
|
|
4965fd5888 | ||
|
|
36db8b6f59 | ||
|
|
9e69f62a39 | ||
|
|
f307021cf1 | ||
|
|
0709c402f8 | ||
|
|
df5deb8b88 | ||
|
|
111dd40097 | ||
|
|
c83e7d7995 | ||
|
|
718f964de7 | ||
|
|
ce0db9b968 | ||
|
|
3f0d602353 | ||
|
|
5364d778b0 | ||
|
|
0ddec227fa | ||
|
|
a5ea35ee56 | ||
|
|
a3e03fb889 | ||
|
|
b7711f0182 | ||
|
|
aae199ff1c | ||
|
|
8d0611a188 | ||
|
|
958b245c3d | ||
|
|
fbb77092eb | ||
|
|
ebc8328712 | ||
|
|
7967c6dfc8 | ||
|
|
fca08a5162 | ||
|
|
49f65d6762 | ||
|
|
7684e1bad5 | ||
|
|
46e974d201 | ||
|
|
ab55d0abc8 | ||
|
|
ed668d5144 | ||
|
|
dec0f28a2b | ||
|
|
de7d79f1f4 | ||
|
|
d204d580dd | ||
|
|
e4e38edf24 | ||
|
|
3721a1f90b | ||
|
|
897850c3eb | ||
|
|
a22efb25c6 | ||
|
|
f7e80879ae | ||
|
|
bf902e6398 | ||
|
|
aed817f724 | ||
|
|
5ac76d29fc | ||
|
|
81311ddd03 | ||
|
|
3c52070243 | ||
|
|
9070f50658 | ||
|
|
ecc3c56cb1 | ||
|
|
a86191f6e0 | ||
|
|
aa5fdac522 | ||
|
|
3cd32c74a1 | ||
|
|
2e2f6a03d9 | ||
|
|
25a073f20b | ||
|
|
0fb244e381 | ||
|
|
631ba70b72 | ||
|
|
0a1d6b2a6f | ||
|
|
94bfa5ea25 | ||
|
|
118c14bf1e | ||
|
|
cf395f707a | ||
|
|
6e040fa87c | ||
|
|
c0a7f84382 | ||
|
|
826d7b7c26 | ||
|
|
91e63d0f19 | ||
|
|
2d69e5f6ff | ||
|
|
23f0fcd024 | ||
|
|
3fcd82abad | ||
|
|
d766c39791 | ||
|
|
a76c3b3baa | ||
|
|
9290b4eecc | ||
|
|
eccfec4b4f | ||
|
|
80aadd3874 | ||
|
|
9da15826db | ||
|
|
8c403f8795 | ||
|
|
b010824dbd | ||
|
|
3340f8864f | ||
|
|
9fb3cdfc4c | ||
|
|
3a3a2c2702 | ||
|
|
be38919758 | ||
|
|
a816ccf6ee | ||
|
|
5e85068e35 | ||
|
|
77865ab4a1 | ||
|
|
5fc6ee27f6 | ||
|
|
5995adf8b6 | ||
|
|
c9f47ed89a | ||
|
|
d6325ffcfa | ||
|
|
9d72472114 | ||
|
|
102350a125 | ||
|
|
1c650cb136 | ||
|
|
625e74653d | ||
|
|
5bad762293 | ||
|
|
fc1ff11352 | ||
|
|
0595ba384e | ||
|
|
f80f5e9955 | ||
|
|
f689014c3e | ||
|
|
9b24655bbf | ||
|
|
c3201070a1 | ||
|
|
8a146209e4 | ||
|
|
2b719726cd | ||
|
|
27209e4836 | ||
|
|
0497762f94 | ||
|
|
b38f1e744b | ||
|
|
481c49e18e | ||
|
|
69cf9115b5 | ||
|
|
3eedd89efe | ||
|
|
ad262ea624 | ||
|
|
08cd8c95f7 | ||
|
|
c86790c6b8 | ||
|
|
e466d9252f | ||
|
|
02f8a0baa6 | ||
|
|
9f7bbe3f9d | ||
|
|
e81e0e97e0 | ||
|
|
52554c3972 | ||
|
|
48b6c4876a | ||
|
|
99e89c2540 | ||
|
|
0e8453d4ed | ||
|
|
c750b746b3 | ||
|
|
0ac07e2e83 | ||
|
|
ead6cb9ca3 | ||
|
|
c401f9211d | ||
|
|
abb72c0a9a | ||
|
|
bc9172bd43 | ||
|
|
b205a5e49d | ||
|
|
fccf9642d8 | ||
|
|
96dd68f52b | ||
|
|
7f923bb389 | ||
|
|
7ab7ebb3f6 | ||
|
|
4eacb58a3e | ||
|
|
24248832d1 | ||
|
|
a53f0900bd | ||
|
|
6a1a5a0f74 | ||
|
|
65aa4e145e | ||
|
|
cf903aa082 | ||
|
|
75dd86e8ef | ||
|
|
70a3d62c14 | ||
|
|
291e3cd229 | ||
|
|
9b28c1fdd2 | ||
|
|
d1d73da039 | ||
|
|
59f041b9a9 | ||
|
|
fbf3b9ce4d | ||
|
|
9be2e1c4c9 | ||
|
|
a06c9f890c | ||
|
|
fc8e8874a9 | ||
|
|
09e0f68616 | ||
|
|
0e7e36f80d | ||
|
|
8484dcd332 | ||
|
|
8a46296b48 | ||
|
|
b748a910b2 | ||
|
|
932603eea3 | ||
|
|
5056972f79 | ||
|
|
d99ccca291 | ||
|
|
1d9b26d9ec | ||
|
|
3a64707c6e | ||
|
|
6f4071bd75 | ||
|
|
1354fdc613 | ||
|
|
991244655a | ||
|
|
0d07d935bf | ||
|
|
bd56b642a7 | ||
|
|
d989f0edf2 | ||
|
|
f561cf9c8f | ||
|
|
13502e5467 | ||
|
|
045c2a6f05 | ||
|
|
725e9feb68 | ||
|
|
6e45126f53 | ||
|
|
6ebc39e038 | ||
|
|
8b0a7c302b | ||
|
|
0c3e50da2f | ||
|
|
d220cf4acc | ||
|
|
eb1ce7e927 | ||
|
|
46f6f41a6b | ||
|
|
2fa59fc43c | ||
|
|
b7763b3887 | ||
|
|
8c002b67d5 | ||
|
|
7692c4a377 | ||
|
|
1f0acec06c | ||
|
|
1b5295a8fe | ||
|
|
0b699a3f20 | ||
|
|
409d7a499d | ||
|
|
ba7428f3d6 | ||
|
|
c12c32d26e | ||
|
|
0ca5ea0150 | ||
|
|
11ca7c7b3e | ||
|
|
8f6c47527d | ||
|
|
dd28882b27 | ||
|
|
1b93a449b2 | ||
|
|
7cf2ec09b7 | ||
|
|
4d1952a406 | ||
|
|
b8d25e2327 | ||
|
|
89d7c7e572 | ||
|
|
75b19230eb | ||
|
|
7889ca1ca7 | ||
|
|
cec1b932bd | ||
|
|
2c7455ea2b | ||
|
|
edc9cc9278 | ||
|
|
51eafaeb4f | ||
|
|
556373d331 | ||
|
|
804cfb7482 | ||
|
|
143eae0729 | ||
|
|
29d4846de7 | ||
|
|
306178f371 | ||
|
|
371b541fbc | ||
|
|
902658fd21 | ||
|
|
0ca8dff974 | ||
|
|
a212b054e7 | ||
|
|
cccd1baa2f | ||
|
|
5dd25aed4d | ||
|
|
6ddc725b75 | ||
|
|
36ab7444a0 | ||
|
|
a8d004334a | ||
|
|
900398a632 | ||
|
|
f24bb8737d | ||
|
|
cb5e9b197a | ||
|
|
450e20ae78 | ||
|
|
5d42a76fe5 | ||
|
|
c5075a3db0 | ||
|
|
ad5bb5198b | ||
|
|
d306bc4a9a | ||
|
|
5c3b956542 | ||
|
|
ade8533f28 | ||
|
|
ad3413d6ad | ||
|
|
f3d0e3f4dd | ||
|
|
6e2e030ba7 | ||
|
|
613f993638 | ||
|
|
2566e3631b | ||
|
|
4ad6c0a8a9 | ||
|
|
0369ac450e | ||
|
|
ab16680cc8 | ||
|
|
8a344cd9b2 | ||
|
|
4e66fb4589 | ||
|
|
0043e3d623 | ||
|
|
ed168015bf | ||
|
|
1d450ef27e | ||
|
|
932e681d22 | ||
|
|
032bcd9fbe | ||
|
|
e113fa53a4 | ||
|
|
da212dc2a4 | ||
|
|
d1c5a62a9d | ||
|
|
0c777e10e5 | ||
|
|
b919b172b1 | ||
|
|
5dd8d05114 | ||
|
|
3400b32015 | ||
|
|
2e4becad60 | ||
|
|
3d485858d5 | ||
|
|
be93643808 | ||
|
|
f704352bae | ||
|
|
6683b46b07 | ||
|
|
a1fe458338 | ||
|
|
e6328e3207 | ||
|
|
8b1f703397 | ||
|
|
583c8add7e | ||
|
|
0266cc8e99 | ||
|
|
fac4132cb1 | ||
|
|
bf3864b2da | ||
|
|
7c21042858 | ||
|
|
d0fd9e4618 | ||
|
|
9b53230f83 | ||
|
|
d814b7b11e | ||
|
|
9f5421d3d4 | ||
|
|
6b9e5db3bf | ||
|
|
9eac3faf3d | ||
|
|
252de444fc | ||
|
|
c29b9cc937 | ||
|
|
6ac582a771 | ||
|
|
04b0c9f2cf | ||
|
|
6b2468f033 | ||
|
|
2737acb114 | ||
|
|
d03e9c48c5 | ||
|
|
08d9cfe82c | ||
|
|
e8ea8f91c3 | ||
|
|
e7cfa7fda5 | ||
|
|
b0b504d002 | ||
|
|
1da07ba60b | ||
|
|
cedebf8145 | ||
|
|
29832bb2c0 | ||
|
|
320d815d60 | ||
|
|
9f18401581 | ||
|
|
b3a2f0e3a3 | ||
|
|
c00dcd31d6 | ||
|
|
b836be2c84 | ||
|
|
f89c5cc072 | ||
|
|
ab7fa56bb3 | ||
|
|
7e624159ee | ||
|
|
857a791d68 | ||
|
|
171463faf5 | ||
|
|
2cafb49ffe | ||
|
|
ae12fed5fe | ||
|
|
be5547de53 | ||
|
|
cac50ef566 | ||
|
|
57a5736942 | ||
|
|
167eadf699 | ||
|
|
5676e420a6 | ||
|
|
f5edc9e973 | ||
|
|
4a305e1568 | ||
|
|
748ab221dd | ||
|
|
7d786cea8f | ||
|
|
1bf734338d | ||
|
|
98df1d6004 | ||
|
|
290e942f3e | ||
|
|
46d54f7e01 | ||
|
|
16e1715d06 | ||
|
|
52faa1bec5 | ||
|
|
c8a6465688 | ||
|
|
0273b7606e | ||
|
|
51022cb132 | ||
|
|
78f9d87ac3 | ||
|
|
e57135eaab | ||
|
|
5fbb663529 | ||
|
|
7db0f540bd | ||
|
|
c8f0afb775 | ||
|
|
51a5815098 | ||
|
|
18bd2eb729 | ||
|
|
f04fd61879 | ||
|
|
c1527cc4c8 | ||
|
|
cf1c1ed126 | ||
|
|
c4ba8f96e6 | ||
|
|
350884c627 | ||
|
|
aa756f6d68 | ||
|
|
1a77bf49f5 | ||
|
|
1b509c56fe | ||
|
|
038b10c74d | ||
|
|
39baa59625 | ||
|
|
3fc091ebb2 | ||
|
|
a557e133d3 | ||
|
|
7d4f10ccc6 | ||
|
|
39a15bef12 | ||
|
|
92c59f3ea1 | ||
|
|
226bd44e2c | ||
|
|
78d43a49c6 | ||
|
|
471651ac5a | ||
|
|
89515b0e30 | ||
|
|
1d61c0d970 | ||
|
|
35c3acbade | ||
|
|
58a2c6a13a | ||
|
|
bf0160adbd | ||
|
|
dfbc92dd3e | ||
|
|
85b0bbfd55 | ||
|
|
fb2a93dc05 | ||
|
|
4ac46bf19d | ||
|
|
4d73adb540 | ||
|
|
bdfeb1ae22 | ||
|
|
3fc116ab53 | ||
|
|
8db61a38cc | ||
|
|
0a70979934 | ||
|
|
1a2ccdf991 | ||
|
|
0adee56d76 | ||
|
|
fedd50efe4 | ||
|
|
d7de7dbe6b | ||
|
|
9fb8939efd | ||
|
|
fe8f671137 | ||
|
|
1a4de1482b | ||
|
|
090a2595e1 | ||
|
|
89dc7f817f | ||
|
|
fe0f26ce9c | ||
|
|
11349629f4 | ||
|
|
9a67652c0c | ||
|
|
b35521dfcb | ||
|
|
9faf306d1e | ||
|
|
d8393dace6 | ||
|
|
692e5275a3 | ||
|
|
1cfe517db8 | ||
|
|
e99e1dc2ac | ||
|
|
1bf4ebcabb | ||
|
|
ec4b264868 | ||
|
|
efb125b2d5 | ||
|
|
1c0fdbed59 | ||
|
|
37bb10e564 | ||
|
|
1507d5d85f | ||
|
|
2a86acfc50 | ||
|
|
e0ee01b92e | ||
|
|
6006917622 | ||
|
|
4ffcf6bf3b | ||
|
|
49cde59c4e | ||
|
|
ed7382995c | ||
|
|
a7faab53b9 | ||
|
|
a6aad25117 | ||
|
|
5f06084b19 | ||
|
|
8151fbcb2e | ||
|
|
5a8e46a6b8 | ||
|
|
c4997c52e2 | ||
|
|
ff12c22f50 | ||
|
|
ed7f09ab80 | ||
|
|
f68941992a | ||
|
|
afcd9285e6 | ||
|
|
22f78d5b33 | ||
|
|
5259142eb6 | ||
|
|
dc403ce8ab | ||
|
|
a6fddd6a2c | ||
|
|
819cd623f9 | ||
|
|
f185494de3 | ||
|
|
628a17ae72 | ||
|
|
08169b9180 | ||
|
|
1e79f2441c | ||
|
|
8680c0ba39 | ||
|
|
8a78b0dbd6 | ||
|
|
0232035e34 | ||
|
|
74f3df83db | ||
|
|
53fa0d2fe8 | ||
|
|
52aa9ab761 | ||
|
|
92cf6195b2 | ||
|
|
86a392991a | ||
|
|
30d426d667 | ||
|
|
5702c50680 | ||
|
|
e38d2e156d | ||
|
|
dfc18619a2 | ||
|
|
9e8f2c52a0 | ||
|
|
1c8f5b98f1 | ||
|
|
972470ad42 | ||
|
|
f7e3a055fb | ||
|
|
34f05fb8dc | ||
|
|
709fd34264 | ||
|
|
b888757092 | ||
|
|
0f42827047 | ||
|
|
e69d1556e1 | ||
|
|
141fbf7472 | ||
|
|
c9a3ae8149 | ||
|
|
e16a8d29ea | ||
|
|
fff50d0e50 | ||
|
|
360734b447 | ||
|
|
7e5edad32b | ||
|
|
fc47b47850 | ||
|
|
c7de058e1d | ||
|
|
0fff3c905c | ||
|
|
2fcb055c43 | ||
|
|
97e0d63102 | ||
|
|
5e04f0accd | ||
|
|
16835e3928 | ||
|
|
22e0ef1354 | ||
|
|
0141dc519c | ||
|
|
9aadc30b94 | ||
|
|
23a73fcb7c | ||
|
|
9a3b585110 | ||
|
|
09ee46860a | ||
|
|
d8f289182d | ||
|
|
dc8fa2153b | ||
|
|
32fb335832 | ||
|
|
58885854f1 | ||
|
|
3e87788541 | ||
|
|
5746df4d74 | ||
|
|
71d86c44e0 | ||
|
|
c5e50289e1 | ||
|
|
e62f1edada | ||
|
|
50e8db28b8 | ||
|
|
ef7c572018 | ||
|
|
1e962754cc | ||
|
|
0240453b07 | ||
|
|
38a80a95f2 | ||
|
|
b5e411d251 | ||
|
|
71a623276e | ||
|
|
1e76703144 | ||
|
|
6ffaf11226 | ||
|
|
fc0caec1a4 | ||
|
|
043f9a5025 | ||
|
|
822264a65e | ||
|
|
8e9a6a4f06 | ||
|
|
76f3909ec9 | ||
|
|
23e839b40e | ||
|
|
6a560fc7bf | ||
|
|
709d1c93a4 | ||
|
|
8d4b5f4bb7 | ||
|
|
9eb5a85e9e | ||
|
|
e7521445e9 | ||
|
|
bb050e2660 | ||
|
|
bc3b0eb491 | ||
|
|
7cea05d376 | ||
|
|
b8a3bdf6b5 | ||
|
|
638b6dba17 | ||
|
|
50ba8c6c39 | ||
|
|
94d15ebbef | ||
|
|
e150882231 | ||
|
|
80a659c90c | ||
|
|
17d96427ea | ||
|
|
7fdda788af | ||
|
|
182adbd9d9 | ||
|
|
4931c9cd9b | ||
|
|
a112409735 | ||
|
|
7aaa6dd986 | ||
|
|
3c39cfe645 | ||
|
|
6a46325e7e | ||
|
|
15c9688a5a | ||
|
|
d9f93ccc11 | ||
|
|
652e569afc | ||
|
|
c55372459f | ||
|
|
09f36e32f7 | ||
|
|
688d435d8e | ||
|
|
e7403d8551 | ||
|
|
60039441a1 | ||
|
|
97f4414cc4 | ||
|
|
df9595bc0d | ||
|
|
93533b7bc6 | ||
|
|
a99afa2ddf | ||
|
|
1e651a5145 | ||
|
|
79ecfa2573 | ||
|
|
e2b676d54c | ||
|
|
2b73bf35b6 | ||
|
|
2137b3a879 | ||
|
|
e52b3fd5bc | ||
|
|
0dfd2aa714 | ||
|
|
5d63c4c981 | ||
|
|
84d3f9ab9a | ||
|
|
1133dba9d8 | ||
|
|
cd2fdc2ffe | ||
|
|
57d017ed8e | ||
|
|
3ad2c559f1 | ||
|
|
c3facfa925 | ||
|
|
db8d9ebc72 | ||
|
|
3aef00af18 | ||
|
|
a4d576bae8 | ||
|
|
9320365ef6 | ||
|
|
0be7494042 | ||
|
|
df55f338ca | ||
|
|
8210efb548 | ||
|
|
43503e2fc9 | ||
|
|
448c3ae425 | ||
|
|
f7684a8a3e | ||
|
|
4f200ac71e | ||
|
|
4772559e8b | ||
|
|
a984ce5bba | ||
|
|
eee8a338e2 | ||
|
|
8894c0ef48 | ||
|
|
c5b8577d94 | ||
|
|
ce9def4a56 | ||
|
|
894601aad2 | ||
|
|
285c91e81b | ||
|
|
ba8ca93afc | ||
|
|
ad0dab2493 | ||
|
|
8b66316180 | ||
|
|
da4ee38914 | ||
|
|
62fde229e2 | ||
|
|
676115d214 | ||
|
|
f36d838df1 | ||
|
|
f32f3ab895 | ||
|
|
fc0432df8c | ||
|
|
797b5bcb53 | ||
|
|
b816f203cd | ||
|
|
714e2f86a1 | ||
|
|
ff75085f15 | ||
|
|
dbc8b18b80 | ||
|
|
efb2bbe8b2 | ||
|
|
8278c8eedd | ||
|
|
6f696a026b | ||
|
|
f558797246 | ||
|
|
c8671d1c2b | ||
|
|
7fd41e48b9 | ||
|
|
409d5f5966 | ||
|
|
5d1802faf7 | ||
|
|
a580bac385 | ||
|
|
c43214543f | ||
|
|
b3c568fb6f | ||
|
|
b34fe3200c | ||
|
|
dada8dbdeb | ||
|
|
e7d4077065 | ||
|
|
b1e54bfd86 | ||
|
|
85ad4c4766 | ||
|
|
3dce560759 | ||
|
|
969b8f0a7a | ||
|
|
ba8e738f40 | ||
|
|
4bc80fae8d | ||
|
|
ee5156704d | ||
|
|
986cd03732 | ||
|
|
937fdabaa9 | ||
|
|
c0fa97510e | ||
|
|
2fc6fe442d | ||
|
|
7ba14f5a57 | ||
|
|
352e4576fc | ||
|
|
210eebc0dc | ||
|
|
5904941361 | ||
|
|
b352815cf8 | ||
|
|
38a2751cd9 | ||
|
|
6388607d11 | ||
|
|
469276b8be | ||
|
|
2a863ee851 | ||
|
|
b6dae2fe57 | ||
|
|
d202bd3e50 | ||
|
|
dc2e44b2c0 | ||
|
|
32c481c28c | ||
|
|
8519c99706 | ||
|
|
5b1bac4c19 | ||
|
|
0a95a0674b | ||
|
|
2f11f6dc37 | ||
|
|
af66274c72 | ||
|
|
c5679b208f | ||
|
|
020fc179a5 | ||
|
|
dc9c1fc762 | ||
|
|
f0938d70b2 | ||
|
|
072761c5ba | ||
|
|
8aacd4d022 | ||
|
|
b6f9ca0021 | ||
|
|
a43f354511 | ||
|
|
43766406ae | ||
|
|
37dd1ae363 | ||
|
|
5a3effaef5 | ||
|
|
cd4bcce70f | ||
|
|
c1e7549b61 | ||
|
|
036ef25da9 | ||
|
|
9884c37ef4 | ||
|
|
23069d76eb | ||
|
|
b2d0e7fae2 | ||
|
|
30a039a031 | ||
|
|
8d67bb726c | ||
|
|
c4dec4cd4d | ||
|
|
f17b975fb9 | ||
|
|
7a3b7593d0 | ||
|
|
bad342b840 | ||
|
|
e3968a8f9d | ||
|
|
f577183c24 | ||
|
|
be58f2e68a | ||
|
|
99f81e82d5 | ||
|
|
7cfd3957de | ||
|
|
c51c88a17c | ||
|
|
e09af98dc5 | ||
|
|
89635f6583 | ||
|
|
14034642f1 | ||
|
|
4af7d975c1 | ||
|
|
d9884917fb | ||
|
|
95fd4e4c99 | ||
|
|
9457a55910 | ||
|
|
56e7dd0de0 | ||
|
|
f0dc882ae9 | ||
|
|
acb70d4440 | ||
|
|
167721bf8c | ||
|
|
4f438e5228 | ||
|
|
0f0aeb6d47 | ||
|
|
d1a6edad8a | ||
|
|
388feb7edb | ||
|
|
962b068c92 | ||
|
|
a32373bc3a | ||
|
|
94a5a4ac1e | ||
|
|
ed8eea12bd | ||
|
|
83bea8b654 | ||
|
|
d4d2db228e | ||
|
|
f8b8c91fe8 | ||
|
|
8451301a5a | ||
|
|
449b03f6bd | ||
|
|
3853ff725f | ||
|
|
ee66e3ef9e | ||
|
|
2643500957 | ||
|
|
88467a87a4 | ||
|
|
d1cdeb4a2d | ||
|
|
f5ced7fe39 | ||
|
|
023267ecab | ||
|
|
3d515a760c | ||
|
|
026c5987dd | ||
|
|
84fb3694f2 | ||
|
|
169dcbcd74 | ||
|
|
d28b6fbf1e | ||
|
|
9f7a2ef84e | ||
|
|
cc5ea60d2b | ||
|
|
8bc63494bc | ||
|
|
9acdeeed04 | ||
|
|
d2cdb2a209 | ||
|
|
36bad31355 | ||
|
|
427587e591 | ||
|
|
4f028d8201 | ||
|
|
d59c052554 | ||
|
|
93ebe91bc5 | ||
|
|
fadb8f695e | ||
|
|
8bb1518d09 | ||
|
|
e8d9d663a7 | ||
|
|
2d2648cf57 | ||
|
|
231036784d | ||
|
|
882b91b221 | ||
|
|
df346ab380 | ||
|
|
c7b8e82ada | ||
|
|
b8784863f2 | ||
|
|
3f67054f03 | ||
|
|
36d1b0a4bc | ||
|
|
748777b8eb | ||
|
|
acd8e0bce5 | ||
|
|
d2d068edff | ||
|
|
e2a7a7ce20 | ||
|
|
b25b1c45fb | ||
|
|
c3b0fa6c93 | ||
|
|
63a4e67174 | ||
|
|
88e37438fa | ||
|
|
5be1bf7d82 | ||
|
|
eb4689d2f1 | ||
|
|
e81549e5fd | ||
|
|
becaef36ff | ||
|
|
6aefa40ca2 | ||
|
|
250ef50361 | ||
|
|
5a1af60357 | ||
|
|
46db75308c | ||
|
|
33f5e13b29 | ||
|
|
54bb4797db | ||
|
|
7a14fbb221 | ||
|
|
18349f5ee4 | ||
|
|
22b93cdd0a | ||
|
|
63bc0b2ba1 | ||
|
|
c57a7c745d | ||
|
|
f2831b5f5c | ||
|
|
96f793f91b | ||
|
|
4e2a0d6e58 | ||
|
|
442c7f1f09 | ||
|
|
dbff4af7c6 | ||
|
|
93b09836da | ||
|
|
1782dc1565 | ||
|
|
cc16874d58 | ||
|
|
c8925b2ae3 | ||
|
|
fc043b9fcd | ||
|
|
ea86d33bc1 | ||
|
|
82f33b82e3 | ||
|
|
c645cb83a2 | ||
|
|
d6d79a2aa5 | ||
|
|
786568fd2f | ||
|
|
434f77a058 | ||
|
|
02f777a143 | ||
|
|
8025a84aeb | ||
|
|
c37a50d3b8 | ||
|
|
6dbbb265d1 | ||
|
|
02ce2ff74c | ||
|
|
28e5267132 | ||
|
|
f498b8a049 | ||
|
|
b31afe5c8c | ||
|
|
eac944dbea | ||
|
|
965b20c37a | ||
|
|
fba85754d7 | ||
|
|
3c69f98451 | ||
|
|
cad8a90124 | ||
|
|
b8d888b17f | ||
|
|
942d783e4e | ||
|
|
0e352cb466 | ||
|
|
50af38b952 | ||
|
|
1459c59cd0 | ||
|
|
e74f5b2991 | ||
|
|
0dec05fd70 | ||
|
|
3cff21b4d0 | ||
|
|
c380da7214 | ||
|
|
e2bf903c49 | ||
|
|
9cf64ba881 | ||
|
|
74d901b7ae | ||
|
|
b15cb46a6d | ||
|
|
89dc76e3b2 | ||
|
|
000b7d2651 | ||
|
|
9ffa372ba2 | ||
|
|
510cfcf1fc | ||
|
|
e7f6d93a1e | ||
|
|
a17c449075 | ||
|
|
c4da7a87f9 | ||
|
|
4a8b2f826d | ||
|
|
4901e4fb80 | ||
|
|
ce3a0157cb | ||
|
|
a146b06e4b | ||
|
|
dc78a3e56d | ||
|
|
86c524dade | ||
|
|
6889edcfc4 | ||
|
|
3cd5934bad | ||
|
|
064104f23a | ||
|
|
e80b1c264c | ||
|
|
03c3ee514b | ||
|
|
c93d0f0d59 | ||
|
|
85793d38bc | ||
|
|
49b4a85d55 | ||
|
|
5e8a9340f1 | ||
|
|
2b97c01bd6 | ||
|
|
39e3a5a708 | ||
|
|
df13d993f7 | ||
|
|
36c62763e5 | ||
|
|
ad5651ffb2 | ||
|
|
717b091b49 | ||
|
|
4376bbe40b | ||
|
|
2382d2e21c | ||
|
|
520ccfcd06 | ||
|
|
0bdc5dfe95 | ||
|
|
6a656a1b0a | ||
|
|
5ad4079dbf | ||
|
|
bfa46b795f | ||
|
|
ecaa9c275c | ||
|
|
93fd1757f8 | ||
|
|
71d0958398 | ||
|
|
ed89d94d7a | ||
|
|
135132e5f0 | ||
|
|
1ad75f5c1b | ||
|
|
21c6f9e394 | ||
|
|
5a7e954aee | ||
|
|
c1117e260c | ||
|
|
b0ea150e94 | ||
|
|
0e3bdc02ea | ||
|
|
e5a075a329 | ||
|
|
30c7a64619 | ||
|
|
ab12f29ebf | ||
|
|
a0c3a82c76 | ||
|
|
085a281f8a | ||
|
|
1f871c24b9 | ||
|
|
9f46b51985 | ||
|
|
fc572b8717 | ||
|
|
4404601fa3 | ||
|
|
faf7e28119 | ||
|
|
0303facfd9 | ||
|
|
86e056a736 | ||
|
|
75105dc1fc | ||
|
|
14b4979c98 | ||
|
|
f956c64765 | ||
|
|
8b3f954ad8 | ||
|
|
a013cfcd1e | ||
|
|
65553e6c43 | ||
|
|
401c881c00 | ||
|
|
cf0e4d24a1 | ||
|
|
21810f3b39 | ||
|
|
0b211ab0ba | ||
|
|
30c7cf3512 | ||
|
|
bf8839a560 | ||
|
|
818998c532 | ||
|
|
98f54029d0 | ||
|
|
d41f2c138b | ||
|
|
50f07a6a0d | ||
|
|
b10116febb | ||
|
|
17b5d732e4 | ||
|
|
5b16b1d827 | ||
|
|
e5fe962130 | ||
|
|
00f03d9bb7 | ||
|
|
f7c285af20 | ||
|
|
f6e3121c76 | ||
|
|
a95aa81850 | ||
|
|
ffa5825658 | ||
|
|
df5b90cc7c | ||
|
|
9dde644fd0 | ||
|
|
d525a1536c | ||
|
|
54f18f3f0d | ||
|
|
ce4f26a5d5 | ||
|
|
177a7429f1 | ||
|
|
f672d734f9 | ||
|
|
faf5a38f1c | ||
|
|
03c4267b84 | ||
|
|
ab08621808 | ||
|
|
d06c0e5c5d | ||
|
|
32e76298e4 | ||
|
|
752163a5c0 | ||
|
|
6568fa23f8 | ||
|
|
9c69288304 | ||
|
|
3a3346e750 | ||
|
|
47ab73d9a4 | ||
|
|
9555385325 | ||
|
|
f80a03afc7 | ||
|
|
c1b64770d1 | ||
|
|
400f106d62 | ||
|
|
1cc20716ae | ||
|
|
fc7d123871 | ||
|
|
2a8a014ad0 | ||
|
|
861f452d26 | ||
|
|
64a7d5e4fd | ||
|
|
7c45423969 | ||
|
|
480f70d7e2 | ||
|
|
7f182896ca | ||
|
|
1319fe2eb5 | ||
|
|
f5c231aa80 | ||
|
|
84d468333a | ||
|
|
dfdbb0a061 | ||
|
|
27365abbe3 | ||
|
|
f3aed46c94 | ||
|
|
7e0ea176cf | ||
|
|
791e13e7bf | ||
|
|
9dff3ac749 | ||
|
|
45f207164d | ||
|
|
bea6f3ec14 | ||
|
|
1edc883ca2 | ||
|
|
116825d604 | ||
|
|
26bc5ef091 | ||
|
|
8c228287aa | ||
|
|
5c7c682d41 | ||
|
|
bfc2eca041 | ||
|
|
b043b48e6e | ||
|
|
d7c03d7356 | ||
|
|
c41f6aebf4 | ||
|
|
5e6cda7c5e | ||
|
|
9c71b53abc | ||
|
|
3f96e04a7b | ||
|
|
90eeeedf52 | ||
|
|
6649362fec | ||
|
|
39c8889585 | ||
|
|
b5b1a64f0c | ||
|
|
cd108b5f57 | ||
|
|
780f3f8552 | ||
|
|
afea5eb69e | ||
|
|
b72cf5d364 | ||
|
|
c133686dde | ||
|
|
e216fa98ef | ||
|
|
8f9d8e188f | ||
|
|
800751cbe0 | ||
|
|
14bc73cde9 | ||
|
|
41307046be | ||
|
|
c259c79384 | ||
|
|
49f69facaf | ||
|
|
da2b6acf91 | ||
|
|
fbdbe848c5 | ||
|
|
cc41caede9 | ||
|
|
ee373eb6fb | ||
|
|
d4b18b1e27 | ||
|
|
94e2fe2cbb | ||
|
|
c93658ee04 | ||
|
|
ad7ee0b716 | ||
|
|
b2fad7a567 | ||
|
|
e80adc2799 | ||
|
|
ac111224ae | ||
|
|
0813bb1a28 | ||
|
|
4d1ad8f749 | ||
|
|
80b444e531 | ||
|
|
3a6468cd0a | ||
|
|
c9fdb215f3 | ||
|
|
85ff5f2d10 | ||
|
|
be3597524f | ||
|
|
5717dbda4f | ||
|
|
588eec4672 | ||
|
|
cc3d0d8211 | ||
|
|
1550efaf7e | ||
|
|
007300b881 | ||
|
|
454f072f13 | ||
|
|
b3be6c75c3 | ||
|
|
f8774f314b | ||
|
|
3db35ad343 | ||
|
|
0e42a779e2 | ||
|
|
d34e829811 | ||
|
|
ebd988702e | ||
|
|
1232398742 | ||
|
|
6dbcd47143 | ||
|
|
0e07482e26 | ||
|
|
6bb4800ecd | ||
|
|
37e1ac71b0 | ||
|
|
869f96816c | ||
|
|
35a7008444 | ||
|
|
67f80ee111 | ||
|
|
083a58753b | ||
|
|
b652357a5c | ||
|
|
49a52140b0 | ||
|
|
e37f84a227 | ||
|
|
50fc83d478 | ||
|
|
a3da1c54eb | ||
|
|
b8471e3cf1 | ||
|
|
4bd645d5c6 | ||
|
|
83446c0694 | ||
|
|
b4c30fc07b | ||
|
|
8325f5bbae | ||
|
|
9eb1196526 | ||
|
|
5fe5d4b6fc | ||
|
|
34af44b8d3 | ||
|
|
643a20fec3 | ||
|
|
576b931090 | ||
|
|
2cc1f044df | ||
|
|
c651392643 | ||
|
|
9a669176ac | ||
|
|
b902831db8 | ||
|
|
90a1de383a | ||
|
|
83f9d42d31 | ||
|
|
94136531a5 | ||
|
|
b8b1e6ba62 | ||
|
|
d6911337f3 | ||
|
|
d2cbb72490 | ||
|
|
c835b4d363 | ||
|
|
ece4480200 | ||
|
|
57f8d90e38 | ||
|
|
da2b2701fe | ||
|
|
6d7fba3abf | ||
|
|
90798c161b | ||
|
|
65e270ff57 | ||
|
|
4912e19281 | ||
|
|
6402564f44 | ||
|
|
a06d5dd7bb | ||
|
|
15e7486ca3 | ||
|
|
ac462cfce9 | ||
|
|
d921306dd8 | ||
|
|
05417705ed | ||
|
|
1c7d9e3d75 | ||
|
|
97dd928ca4 | ||
|
|
03949f7d60 | ||
|
|
effe477351 | ||
|
|
12ac858242 | ||
|
|
eec1937f52 | ||
|
|
906cd48fb7 | ||
|
|
78beecf6f1 | ||
|
|
06a0646541 | ||
|
|
0499a6c6f9 | ||
|
|
0e62f2ee85 | ||
|
|
2ba4c36af0 | ||
|
|
688e6ad79f | ||
|
|
f8ee0ce185 | ||
|
|
a226e06ae8 | ||
|
|
e0e51b51b7 | ||
|
|
9e9e3d43a8 | ||
|
|
7691a9bdad | ||
|
|
22020cae49 | ||
|
|
6cd910b0f6 | ||
|
|
b5e9811063 | ||
|
|
7668c7dd00 | ||
|
|
d71f743e85 | ||
|
|
73ef0222ac | ||
|
|
6152387096 | ||
|
|
b93ffe3b44 | ||
|
|
478eb7c5b5 | ||
|
|
6c40d8bd8b | ||
|
|
7026fe4d5a | ||
|
|
9a4491dc50 | ||
|
|
0d835e5014 | ||
|
|
29b5ffb58d | ||
|
|
a179ec485b | ||
|
|
f220a577e0 | ||
|
|
b22b1d4794 | ||
|
|
d2d19e3fbc | ||
|
|
19ef290499 | ||
|
|
f6584b45ed | ||
|
|
bad97a8b5b | ||
|
|
7116df15d6 | ||
|
|
ffbb018d12 | ||
|
|
a4dcdfc403 | ||
|
|
87d901b1ae | ||
|
|
9126651cb9 | ||
|
|
3996ce8ce5 | ||
|
|
6340423b32 | ||
|
|
0d921dc1fc | ||
|
|
25afea4f9c | ||
|
|
ba46bf3af5 | ||
|
|
416b4885f9 | ||
|
|
9a10ee0efa | ||
|
|
d63b78f0e7 | ||
|
|
99411241f6 | ||
|
|
941fb1349b | ||
|
|
a470925305 | ||
|
|
e3ac292af1 | ||
|
|
260b9a8d0d | ||
|
|
530407f9b2 | ||
|
|
52e7b50e9b | ||
|
|
23aa097d0a | ||
|
|
0bea153aa0 | ||
|
|
00be58e65d | ||
|
|
a5cc985bc2 | ||
|
|
afba54fb5e | ||
|
|
46150a2da4 | ||
|
|
842988c2ad | ||
|
|
ebad34ae51 | ||
|
|
bceb359438 | ||
|
|
19a32134ab | ||
|
|
e44a71daf2 | ||
|
|
573750a43d | ||
|
|
dd4c73b41f | ||
|
|
094326220f | ||
|
|
729a071284 | ||
|
|
ee7857a560 | ||
|
|
4cba604712 | ||
|
|
18a62797d9 | ||
|
|
5d4ac62c9e | ||
|
|
2ef8c41b48 | ||
|
|
891831a96a | ||
|
|
11d18fa1af | ||
|
|
ad69cbc681 | ||
|
|
10995de7bd | ||
|
|
c4d78c06eb | ||
|
|
eb252321a7 | ||
|
|
04dd1447ee | ||
|
|
b14edbe1f4 | ||
|
|
c697e52c0c | ||
|
|
b6911da8f0 | ||
|
|
038448865b | ||
|
|
9e44fdbc55 | ||
|
|
e088f0f31e | ||
|
|
6101feda10 | ||
|
|
7a3fa68468 | ||
|
|
16a11638a7 | ||
|
|
43932492a7 | ||
|
|
8d08ffc4ae | ||
|
|
a5a4ee4bfe | ||
|
|
e66f3a80e8 | ||
|
|
e69efacb47 | ||
|
|
528b361e34 | ||
|
|
a77bb3d341 | ||
|
|
6b17385ad7 | ||
|
|
33fc01ef4b | ||
|
|
482324c0bd | ||
|
|
0ab13a3a70 | ||
|
|
699fb73726 | ||
|
|
ddba29cc09 | ||
|
|
1cdb9ef47c | ||
|
|
ade3305b4c | ||
|
|
bd2bd37bc4 | ||
|
|
ee6f55193e | ||
|
|
1868df61ea | ||
|
|
dfa1fc7b09 | ||
|
|
a52b0495f1 | ||
|
|
ce00eee29f | ||
|
|
858bfade6a | ||
|
|
4535cf9274 | ||
|
|
84d1fa54ec | ||
|
|
7cf4721efa | ||
|
|
6578838587 | ||
|
|
46b7c4717f | ||
|
|
52bcf63554 | ||
|
|
37ec97550e | ||
|
|
55e20abea8 | ||
|
|
ec7d81dc7a | ||
|
|
e93387b719 | ||
|
|
050b476fc3 | ||
|
|
5b5ffd7eaf | ||
|
|
39bf5fdc05 | ||
|
|
09ff43ef0a | ||
|
|
5a02aeaa89 | ||
|
|
2a3a91af42 | ||
|
|
d4a64e93e0 | ||
|
|
c126b64892 | ||
|
|
526814b9ed | ||
|
|
bc3b790f13 | ||
|
|
1fb0765443 | ||
|
|
c4b85f9bda | ||
|
|
161b5530b9 | ||
|
|
e03979ebdc | ||
|
|
356c0ab594 | ||
|
|
85152bac34 | ||
|
|
c832888d56 | ||
|
|
cac1b87b44 | ||
|
|
30f71b2ed6 | ||
|
|
9f8e9a1916 | ||
|
|
8519fe57ae | ||
|
|
1442a3853c | ||
|
|
2658ddf868 | ||
|
|
f95eecfcc6 | ||
|
|
1e598cca31 | ||
|
|
f3eaeb96f0 | ||
|
|
91fac8049c | ||
|
|
b1325a7514 | ||
|
|
150b83b4f3 | ||
|
|
3a3a5dc071 | ||
|
|
9b76a5bd66 | ||
|
|
850a1f9155 | ||
|
|
ef6c8a38d0 | ||
|
|
e228cb6648 | ||
|
|
3a53cac607 | ||
|
|
df4bd57c82 | ||
|
|
de737fc72d | ||
|
|
ccea7a5783 | ||
|
|
5b84e1528a | ||
|
|
7382addfe4 | ||
|
|
b8dd6822ac | ||
|
|
8c47b85b9b | ||
|
|
4d2666a0e3 | ||
|
|
fc18eaa292 | ||
|
|
22199cdd87 | ||
|
|
76820f25c6 | ||
|
|
bac4984496 | ||
|
|
1414c5feac | ||
|
|
c344f63154 | ||
|
|
77ea28bef2 | ||
|
|
bf63b15e07 | ||
|
|
ce05504278 | ||
|
|
95b90974a2 | ||
|
|
1adbef7352 | ||
|
|
3109928dcd | ||
|
|
24dd63789c | ||
|
|
27225e6b5e | ||
|
|
2994978fdb | ||
|
|
ffd864dc72 | ||
|
|
53a2c88d4b | ||
|
|
444ae73ffb | ||
|
|
9f5f4b9c91 | ||
|
|
ffd2af0257 | ||
|
|
b983845745 | ||
|
|
cfcc6e1029 | ||
|
|
6fea4377b2 | ||
|
|
e01230e425 | ||
|
|
f66db81edd | ||
|
|
a47471d0ae | ||
|
|
06b5a91cd3 | ||
|
|
8aacf3497e | ||
|
|
7fc5e304e3 | ||
|
|
65536febd5 | ||
|
|
60f7910840 | ||
|
|
829c3c612e | ||
|
|
355c7b59aa | ||
|
|
6508cdc823 | ||
|
|
9ef9f603a3 | ||
|
|
e76f881c67 | ||
|
|
4292d2ce86 | ||
|
|
ae927c5600 | ||
|
|
57e2eed771 | ||
|
|
ca807b892f | ||
|
|
9bc015d10e | ||
|
|
59b204c3bc | ||
|
|
4ac2472a62 | ||
|
|
054b9c2402 | ||
|
|
96f267cbfd | ||
|
|
2018163107 | ||
|
|
253b5b1fb7 | ||
|
|
0be2f1b295 | ||
|
|
a8838fc611 | ||
|
|
d02636e370 | ||
|
|
f3c1b09188 | ||
|
|
7bac69f360 | ||
|
|
5a3fdd225e | ||
|
|
4660f58653 | ||
|
|
5fabca3616 | ||
|
|
edb74dae87 | ||
|
|
31b833b46a | ||
|
|
21bc1c51ba | ||
|
|
4f6cab9a45 | ||
|
|
33626fa47c | ||
|
|
9fcc1e17f2 | ||
|
|
a5f48f8af8 | ||
|
|
3dfa526ec6 | ||
|
|
dc28066017 | ||
|
|
2f2edfbde8 | ||
|
|
3beac98499 | ||
|
|
f70ce9dd26 | ||
|
|
e301b9d399 | ||
|
|
42f92ecd7a | ||
|
|
27eb0d04b9 | ||
|
|
112d13c127 | ||
|
|
16afc9d0f8 | ||
|
|
5b4889b46b | ||
|
|
f7eed0e2af | ||
|
|
cce287e8a6 | ||
|
|
9a001ebacc | ||
|
|
79ab4c02bb | ||
|
|
b6de70a3a6 | ||
|
|
15cafe2d78 | ||
|
|
90197abc5c | ||
|
|
2147436a2a | ||
|
|
8ef0c66966 | ||
|
|
2779ad6c6f | ||
|
|
f30fe3c746 | ||
|
|
f71496693f | ||
|
|
e4cd5dbf1f | ||
|
|
ffd5defcb6 | ||
|
|
0b81421124 | ||
|
|
0f73b9e07f | ||
|
|
d3078699d6 | ||
|
|
cc39f9f891 | ||
|
|
70608df843 | ||
|
|
e7f2c6b5ba | ||
|
|
420fc9aa24 | ||
|
|
200012fd41 | ||
|
|
e3c858278b | ||
|
|
1ec7c3afa4 | ||
|
|
6ab0b60f12 | ||
|
|
5ec38b44b0 | ||
|
|
39c2294d43 | ||
|
|
3b92481133 | ||
|
|
6b8d24d2f5 | ||
|
|
62d6ff61ab | ||
|
|
f7c056145a | ||
|
|
451f54b036 | ||
|
|
ef378de883 | ||
|
|
2f2b63da4a | ||
|
|
01d8f2c7e6 | ||
|
|
330d553ffb | ||
|
|
f1d0895eb1 | ||
|
|
4eaf6415b7 | ||
|
|
f0650bf64f | ||
|
|
44856c6207 | ||
|
|
dcbf6e43c7 | ||
|
|
0d22da672d | ||
|
|
a62471b9b8 | ||
|
|
f673f2c5bc | ||
|
|
a7caae5c17 | ||
|
|
d0deefd0d7 | ||
|
|
9810da6982 | ||
|
|
cb93a3bee3 | ||
|
|
aeacc3e39d | ||
|
|
e1c861cc8a | ||
|
|
37ee05a576 | ||
|
|
07a2bd817d | ||
|
|
78c171e0c2 | ||
|
|
1ae436cb08 | ||
|
|
b0d3a84d1e | ||
|
|
1acd9ca981 | ||
|
|
72467b6b9c | ||
|
|
862c85387c | ||
|
|
eeeca3e21c | ||
|
|
4ee4af7fd4 | ||
|
|
d131cc1871 | ||
|
|
bd21ee318d | ||
|
|
02e7fc0bca | ||
|
|
51ba46970e | ||
|
|
9d65b44ab7 | ||
|
|
350d51a92d | ||
|
|
2a8202caa7 | ||
|
|
9e4591554f | ||
|
|
e98ecfd1f9 | ||
|
|
1cbf5c4359 | ||
|
|
0446faeb6f | ||
|
|
0417ea23c3 | ||
|
|
de9886e011 | ||
|
|
770e57c361 | ||
|
|
d80a00701d | ||
|
|
62d3cd8795 | ||
|
|
ad9ec22fd1 | ||
|
|
293985ec10 | ||
|
|
105598ac8e | ||
|
|
ff93f2220a | ||
|
|
33fddcb289 | ||
|
|
6a09bf2c54 | ||
|
|
2913f5bbbe | ||
|
|
671bdb09b8 | ||
|
|
353a0d14a4 | ||
|
|
e0fd83211b | ||
|
|
2c94d9cc37 | ||
|
|
f7dec412ec | ||
|
|
f7c756d215 | ||
|
|
617635a989 | ||
|
|
ea3b2c4713 | ||
|
|
15455a7e68 | ||
|
|
ad5cedc08c | ||
|
|
328f0c6da8 | ||
|
|
8ef818f581 | ||
|
|
48dace64e4 | ||
|
|
ab5837c7c4 | ||
|
|
9fbdbc1844 | ||
|
|
54129cecc2 | ||
|
|
69c514d1a4 | ||
|
|
7047d12eba | ||
|
|
8ab61637f0 | ||
|
|
7e0bd92593 | ||
|
|
99d9a9f44e | ||
|
|
9c55111e14 | ||
|
|
b1d6ba57ad | ||
|
|
47e9330646 | ||
|
|
e16a3d503c | ||
|
|
dc9c93b347 | ||
|
|
faceff620a | ||
|
|
a5a6a58556 | ||
|
|
45fdab27c9 | ||
|
|
8b360479f4 | ||
|
|
b170f2f69a | ||
|
|
7f1abab9ae | ||
|
|
2ec7bc878e | ||
|
|
e97d9bb095 | ||
|
|
2f3b02e870 | ||
|
|
11b7a45c32 | ||
|
|
fb10d2f9c2 | ||
|
|
60bb7f66cf | ||
|
|
ad5143169a | ||
|
|
d68c90a429 | ||
|
|
8a51549395 | ||
|
|
9f997fe1fa | ||
|
|
00f1f6d86b | ||
|
|
be72728000 | ||
|
|
f558126854 | ||
|
|
08ed1500ac | ||
|
|
f0926f1cc7 | ||
|
|
fe3f6aa22b | ||
|
|
1325f75e81 | ||
|
|
4b1d3a7b6f | ||
|
|
404afcdfd6 | ||
|
|
99793180f1 | ||
|
|
c91f9beddb | ||
|
|
c9c5ccaa6d | ||
|
|
da42de7ac8 | ||
|
|
3c9a6ad218 | ||
|
|
f0389d6b47 | ||
|
|
76eab21738 | ||
|
|
3b32c57d94 | ||
|
|
d119032cb9 | ||
|
|
397a04b49c | ||
|
|
d32c8e9457 | ||
|
|
ebb2974ca7 | ||
|
|
cbb1131a5d | ||
|
|
d9de7fe3b0 | ||
|
|
24ba692884 | ||
|
|
960bbbde29 | ||
|
|
eb79b79c35 | ||
|
|
8212ed6d87 | ||
|
|
c639cb5285 | ||
|
|
a88fd1c669 | ||
|
|
b053f23d15 | ||
|
|
9d32a89491 | ||
|
|
cb9a147fff | ||
|
|
c9fddedbf1 | ||
|
|
530bbbe4c5 | ||
|
|
73e5a9f45b | ||
|
|
72d4e947b6 | ||
|
|
41750c3639 | ||
|
|
52b36f38a0 | ||
|
|
58ceb454dd | ||
|
|
9e5a2db4c5 | ||
|
|
781f283c89 | ||
|
|
f1cd085727 | ||
|
|
3aba855bd6 | ||
|
|
7e3abb27a4 | ||
|
|
f0c11daa37 | ||
|
|
167c174f4d | ||
|
|
0173060eaf | ||
|
|
4edd25ff5d | ||
|
|
2a4e098645 | ||
|
|
7c5019c30f | ||
|
|
c13fe3feb6 | ||
|
|
0ced60e712 | ||
|
|
d0764d5854 | ||
|
|
f7905e3bc2 | ||
|
|
3e770792a4 | ||
|
|
8f9934a7f4 | ||
|
|
b921e7d595 | ||
|
|
a9cb2d2efd | ||
|
|
fe519b3acf | ||
|
|
30cc4a1668 | ||
|
|
a71e4d8970 | ||
|
|
66e7141415 | ||
|
|
6f0cdc864f | ||
|
|
4f9deaaf09 | ||
|
|
380745a814 | ||
|
|
ce4e335412 | ||
|
|
274b8181d1 | ||
|
|
767bdcc2ce | ||
|
|
c00cde8bcc | ||
|
|
5859a18b3a | ||
|
|
375124642d | ||
|
|
af9e6dea68 | ||
|
|
78f2267b06 | ||
|
|
0a34f22240 | ||
|
|
b0651fcaed | ||
|
|
d59faa2a08 | ||
|
|
6a3bc37dc1 | ||
|
|
2ea7796b26 | ||
|
|
cf0057bac4 | ||
|
|
fafb325a88 | ||
|
|
6b69b4671f | ||
|
|
eaa95ee840 | ||
|
|
a86ba1fdf6 | ||
|
|
09e0d2d15b | ||
|
|
6d9fa6418d | ||
|
|
6010f5b7a6 | ||
|
|
9c164e1cea | ||
|
|
880fba8dce | ||
|
|
17d57ee49a | ||
|
|
234949a627 | ||
|
|
ff34671526 | ||
|
|
6e22983d98 | ||
|
|
18254c30ec | ||
|
|
922c955239 | ||
|
|
618a325057 | ||
|
|
d81cb7f866 | ||
|
|
f71a4d5381 | ||
|
|
282cb60a66 | ||
|
|
58dbac4422 | ||
|
|
53e228465b | ||
|
|
06426e4fd7 | ||
|
|
c55a97c24d | ||
|
|
0d61289bf3 | ||
|
|
bf10cbc70b | ||
|
|
2d945e426b | ||
|
|
13250887fa | ||
|
|
3507c22968 | ||
|
|
cc3f98ebcd | ||
|
|
4116e2d6ac | ||
|
|
48eab6b4a7 | ||
|
|
25866b5369 | ||
|
|
c4dd06fa45 | ||
|
|
4846d211e0 | ||
|
|
a586ad1237 | ||
|
|
b701c9c464 | ||
|
|
d0f20ed2bf | ||
|
|
0f64a2f3b0 | ||
|
|
bd83fba6cd | ||
|
|
15b9255a25 | ||
|
|
6794d21487 | ||
|
|
118c4432ec | ||
|
|
ccda429bf1 | ||
|
|
590937d2a0 | ||
|
|
241827dd80 | ||
|
|
77b4e0b4bd | ||
|
|
6a53cb32d8 | ||
|
|
f12ff3b15e | ||
|
|
69ad8676b7 | ||
|
|
d0fd756f6b | ||
|
|
7f8ab67e9e | ||
|
|
0e68bb3d2c | ||
|
|
d5b3933752 | ||
|
|
0b7206a92d | ||
|
|
e875f306fd | ||
|
|
8ed5e5ee28 | ||
|
|
948b0d40f6 | ||
|
|
48f4feb7a7 | ||
|
|
ddc0a32bbc | ||
|
|
10a7c54364 | ||
|
|
a66fa9d844 | ||
|
|
a16f2ac15c | ||
|
|
b699797a40 | ||
|
|
a6e9520d06 | ||
|
|
f8310b1296 | ||
|
|
353bf99cd5 | ||
|
|
a478e4acc9 | ||
|
|
d810e0ba7d | ||
|
|
1e51ffdb72 | ||
|
|
7da1d1fc64 | ||
|
|
957697c383 | ||
|
|
107eb72225 | ||
|
|
810b77f9da | ||
|
|
5e3bc2049b | ||
|
|
229500347d | ||
|
|
1f461db0a1 | ||
|
|
6c55aafee3 | ||
|
|
d57e55b719 | ||
|
|
bf772e5fe5 | ||
|
|
413f3356ce | ||
|
|
f147326fe0 | ||
|
|
30c59644b6 | ||
|
|
3fa5acf2e6 | ||
|
|
3e6ded8823 | ||
|
|
612017aaa2 | ||
|
|
ef57d7e7b6 | ||
|
|
a8d0db5036 | ||
|
|
ad6b5f8fc1 | ||
|
|
e3cb6a0aec | ||
|
|
cbf18a7dc4 | ||
|
|
01559410a9 | ||
|
|
f2ab94fa7f | ||
|
|
d7394a8369 | ||
|
|
9feef11d6f | ||
|
|
dc4e5ddc3c | ||
|
|
8600781bb6 | ||
|
|
bdc7bbdc9d | ||
|
|
73badef594 | ||
|
|
9474e6c08c | ||
|
|
653ded0e6f | ||
|
|
e34bec7dee | ||
|
|
610b560fb5 | ||
|
|
0a03ddb8a7 | ||
|
|
f31790631a | ||
|
|
e07128760e | ||
|
|
dd02ae313d | ||
|
|
d14b0b6843 | ||
|
|
f763c8a777 | ||
|
|
4231b040d8 | ||
|
|
67c587e673 | ||
|
|
3978e24fd8 | ||
|
|
c45de0c032 | ||
|
|
ff935efea1 | ||
|
|
808464f47d | ||
|
|
c986a6c4dd | ||
|
|
17c0479343 | ||
|
|
d93238912a | ||
|
|
d6ef0956e6 | ||
|
|
05db0aad29 | ||
|
|
89bbdfa1fe | ||
|
|
871bda6198 | ||
|
|
20732c9206 | ||
|
|
2a34a3ebb6 | ||
|
|
3f04247a53 | ||
|
|
ca0a1f8f8b | ||
|
|
d8a9f0ca12 | ||
|
|
c2116b841e | ||
|
|
700cf69f18 | ||
|
|
ed5dee5218 | ||
|
|
b224dfdfac | ||
|
|
e7da68547f | ||
|
|
9a785ceb2e | ||
|
|
fca1cd5a1c | ||
|
|
24b862e32e | ||
|
|
2ec9043cf2 | ||
|
|
1102d63469 | ||
|
|
b89f39d78c | ||
|
|
7ba479c9c9 | ||
|
|
8ad6a2980c | ||
|
|
d3b6ed78d9 | ||
|
|
8bd5605c2a | ||
|
|
08dc2fcf33 | ||
|
|
caa8d16371 | ||
|
|
bce92b3d85 | ||
|
|
67858bf300 | ||
|
|
7157e7e77d | ||
|
|
cf5074bdc5 | ||
|
|
fda44063ce | ||
|
|
f9becda02c | ||
|
|
fb9fc952c6 | ||
|
|
18451edfe9 | ||
|
|
8c73cac72f | ||
|
|
c54cedf14b | ||
|
|
8ef4cdc9c3 | ||
|
|
c0213e84f6 | ||
|
|
29de6d89d4 | ||
|
|
c9bf38ce36 | ||
|
|
338eb75bab | ||
|
|
31b1b453b0 | ||
|
|
aaf0e145eb | ||
|
|
089b3e13fd | ||
|
|
e9da2ce12a | ||
|
|
92048ac17b | ||
|
|
5e8561a578 | ||
|
|
d2f5e13074 | ||
|
|
820178f006 | ||
|
|
0a36a91e6d | ||
|
|
5013a92795 | ||
|
|
d81ecfec32 | ||
|
|
e99d7e2c3c | ||
|
|
d417984ff3 | ||
|
|
d38b3e641b | ||
|
|
28ce491dd5 | ||
|
|
c260d72125 | ||
|
|
d1d1b3156d | ||
|
|
472064b751 | ||
|
|
95ab9a0b70 | ||
|
|
4b03f6a039 | ||
|
|
c3460727fa | ||
|
|
2cc1850212 | ||
|
|
2d7443acaf | ||
|
|
13d0b0940c | ||
|
|
c101797924 | ||
|
|
83b55f8e3f | ||
|
|
b3b6362cd9 | ||
|
|
fc9af32d5f | ||
|
|
4cd1025011 | ||
|
|
5233fe8abc | ||
|
|
041e31ea78 | ||
|
|
7a3e881099 | ||
|
|
631bf42f84 | ||
|
|
1f704a7019 | ||
|
|
d295c88474 | ||
|
|
1dd9da4dff | ||
|
|
f2eb0c8427 | ||
|
|
c8ba11faf8 | ||
|
|
a2e243d992 | ||
|
|
c588fff5ca | ||
|
|
87f9599fea | ||
|
|
0459599b1d | ||
|
|
9447b1a696 | ||
|
|
0ccb7443c2 | ||
|
|
02cf27091f | ||
|
|
fdfbd04503 | ||
|
|
866c18200a | ||
|
|
c1cada49d4 | ||
|
|
7bf550a75f | ||
|
|
9c540c03aa | ||
|
|
b3df46db19 | ||
|
|
7ca615a1c1 | ||
|
|
c83db557a6 | ||
|
|
d54594f11d | ||
|
|
434e38608f | ||
|
|
871f090ca0 | ||
|
|
d1d235e025 | ||
|
|
e822a5fd53 | ||
|
|
7b82a4ae50 | ||
|
|
c532e9f2eb | ||
|
|
3fd034816e | ||
|
|
bb4b868c79 | ||
|
|
3b3da11a36 | ||
|
|
f2cbb5306b | ||
|
|
94ede1b324 | ||
|
|
0367248338 | ||
|
|
936db30e58 | ||
|
|
4822f0dd11 | ||
|
|
456d220829 | ||
|
|
b459ba6ea7 | ||
|
|
a19ef9bd16 |
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: skypjack
|
||||
patreon:
|
||||
open_collective:
|
||||
ko_fi:
|
||||
tidelift:
|
||||
community_bridge:
|
||||
liberapay:
|
||||
issuehunt:
|
||||
otechie:
|
||||
custom: https://www.paypal.me/skypjack
|
||||
91
.github/workflows/build.yml
vendored
Normal file
91
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
name: build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
|
||||
linux:
|
||||
timeout-minutes: 10
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
compiler: [
|
||||
g++-7, g++-8, g++-9, g++,
|
||||
clang++-8, clang++-9, clang++
|
||||
]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install g++-7
|
||||
if: ${{ matrix.compiler == 'g++-7' }}
|
||||
run: sudo apt install g++-7
|
||||
- name: Install g++-8
|
||||
if: ${{ matrix.compiler == 'g++-8' }}
|
||||
run: sudo apt install g++-8
|
||||
- name: Install clang-8
|
||||
if: ${{ matrix.compiler == 'clang++-8' }}
|
||||
run: sudo apt install clang-8
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
env:
|
||||
CXX: ${{ matrix.compiler }}
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
|
||||
make -j4
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 10 -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 -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ${{ matrix.toolset_option }} ..
|
||||
cmake --build . -j 4
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 10 -C Debug -j4
|
||||
|
||||
macos:
|
||||
timeout-minutes: 10
|
||||
runs-on: macOS-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
|
||||
make -j4
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 10 -C Debug -j4
|
||||
38
.github/workflows/coverage.yml
vendored
Normal file
38
.github/workflows/coverage.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: coverage
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
|
||||
codecov:
|
||||
timeout-minutes: 10
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
env:
|
||||
CXXFLAGS: "--coverage -fno-inline"
|
||||
CXX: g++
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
|
||||
make -j4
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 10 -C Debug -j4
|
||||
- name: Collect data
|
||||
working-directory: build
|
||||
run: |
|
||||
sudo apt install lcov
|
||||
lcov -c -d . -o coverage.info
|
||||
lcov -l coverage.info
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: build/coverage.info
|
||||
name: EnTT
|
||||
fail_ci_if_error: true
|
||||
39
.github/workflows/deploy.yml
vendored
Normal file
39
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: deploy
|
||||
|
||||
on:
|
||||
release:
|
||||
types: published
|
||||
|
||||
jobs:
|
||||
|
||||
homebrew-entt:
|
||||
timeout-minutes: 5
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
GH_REPO: homebrew-entt
|
||||
FORMULA: entt.rb
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Clone repository
|
||||
working-directory: build
|
||||
env:
|
||||
PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
|
||||
run: git clone https://$GITHUB_ACTOR:$PERSONAL_ACCESS_TOKEN@github.com/$GITHUB_ACTOR/$GH_REPO.git
|
||||
- name: Prepare formula
|
||||
working-directory: build
|
||||
run: |
|
||||
cd $GH_REPO
|
||||
curl "https://github.com/${{ github.repository }}/archive/${{ github.ref }}.tar.gz" --location --fail --silent --show-error --output archive.tar.gz
|
||||
sed -i -e '/url/s/".*"/"'$(echo "https://github.com/${{ github.repository }}/archive/${{ github.ref }}.tar.gz" | sed -e 's/[\/&]/\\&/g')'"/' $FORMULA
|
||||
sed -i -e '/sha256/s/".*"/"'$(openssl sha256 archive.tar.gz | cut -d " " -f 2)'"/' $FORMULA
|
||||
- name: Update remote
|
||||
working-directory: build
|
||||
run: |
|
||||
cd $GH_REPO
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "$GITHUB_ACTOR"
|
||||
git add $FORMULA
|
||||
git commit -m "Update to ${{ github.ref }}"
|
||||
git push origin master
|
||||
29
.github/workflows/sanitizer.yml
vendored
Normal file
29
.github/workflows/sanitizer.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: sanitizer
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
|
||||
linux:
|
||||
timeout-minutes: 10
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
compiler: [clang++]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
env:
|
||||
CXX: ${{ matrix.compiler }}
|
||||
run: |
|
||||
cmake -DENTT_USE_SANITIZER=ON -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
|
||||
make -j4
|
||||
- name: Run tests
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest --timeout 10 -C Debug -j4
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -1,2 +1,12 @@
|
||||
# Conan
|
||||
conan/test_package/build
|
||||
|
||||
# IDEs
|
||||
*.user
|
||||
TODO
|
||||
.idea
|
||||
.vscode
|
||||
.vs
|
||||
CMakeSettings.json
|
||||
|
||||
# Bazel
|
||||
/bazel-*
|
||||
|
||||
73
.travis.yml
73
.travis.yml
@@ -1,73 +0,0 @@
|
||||
language: cpp
|
||||
dist: trusty
|
||||
sudo: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-6']
|
||||
env: COMPILER=g++-6
|
||||
- os: linux
|
||||
compiler: 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-4.0']
|
||||
packages: ['clang-4.0', 'libstdc++-4.9-dev']
|
||||
env: COMPILER=clang++-4.0
|
||||
- os: linux
|
||||
compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-5.0']
|
||||
packages: ['clang-5.0', 'libstdc++-4.9-dev']
|
||||
env: COMPILER=clang++-5.0
|
||||
- os: osx
|
||||
osx_image: xcode8.3
|
||||
compiler: clang
|
||||
env: COMPILER=clang++
|
||||
- os: osx
|
||||
osx_image: xcode9.1
|
||||
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
|
||||
|
||||
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 .. && make -j4
|
||||
- CTEST_OUTPUT_ON_FAILURE=1 make test
|
||||
48
AUTHORS
48
AUTHORS
@@ -1,7 +1,51 @@
|
||||
# Author
|
||||
|
||||
Michele Caini aka skypjack
|
||||
skypjack
|
||||
|
||||
# Contributors
|
||||
|
||||
Paolo Monteverde aka morbo84
|
||||
alexames
|
||||
BenediktConze
|
||||
bjadamson
|
||||
ceeac
|
||||
ColinH
|
||||
corystegel
|
||||
Croydon
|
||||
cugone
|
||||
dbacchet
|
||||
dBagrat
|
||||
djarek
|
||||
DonKult
|
||||
drglove
|
||||
eliasdaler
|
||||
erez-o
|
||||
eugeneko
|
||||
gale83
|
||||
ghost
|
||||
grdowns
|
||||
Green-Sky
|
||||
Innokentiy-Alaytsev
|
||||
Kerndog73
|
||||
Koward
|
||||
Lawrencemm
|
||||
markand
|
||||
mhammerc
|
||||
Milerius
|
||||
morbo84
|
||||
m-waka
|
||||
netpoetica
|
||||
NixAJ
|
||||
Oortonaut
|
||||
Paolo-Oliverio
|
||||
pgruenbacher
|
||||
prowolf
|
||||
stefanofiorentino
|
||||
suVrik
|
||||
szunhammer
|
||||
The5-1
|
||||
vblanco20-1
|
||||
willtunnels
|
||||
WizardIke
|
||||
w1th0utnam3
|
||||
xissburg
|
||||
zaucy
|
||||
|
||||
14
BUILD.bazel
Normal file
14
BUILD.bazel
Normal file
@@ -0,0 +1,14 @@
|
||||
_msvc_copts = ["/std:c++17"]
|
||||
_gcc_copts = ["-std=c++17"]
|
||||
|
||||
cc_library(
|
||||
name = "entt",
|
||||
visibility = ["//visibility:public"],
|
||||
strip_include_prefix = "src",
|
||||
hdrs = glob(["src/**/*.h", "src/**/*.hpp"]),
|
||||
copts = select({
|
||||
"@bazel_tools//src/conditions:windows": _msvc_copts,
|
||||
"@bazel_tools//src/conditions:windows_msvc": _msvc_copts,
|
||||
"//conditions:default": _gcc_copts,
|
||||
}),
|
||||
)
|
||||
195
CMakeLists.txt
195
CMakeLists.txt
@@ -2,7 +2,7 @@
|
||||
# EnTT
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
cmake_minimum_required(VERSION 3.12.4)
|
||||
|
||||
#
|
||||
# Building in-tree is not allowed (we take care of your craziness).
|
||||
@@ -12,75 +12,169 @@ 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 2.4.2)
|
||||
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(VERBOSE "*")
|
||||
message(VERBOSE "* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
|
||||
message(VERBOSE "* Copyright (c) 2017-2021 Michele Caini <michele.caini@gmail.com>")
|
||||
message(VERBOSE "*")
|
||||
|
||||
message("*")
|
||||
message("* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
|
||||
message("* Copyright (c) 2018 ${PROJECT_AUTHOR} <${PROJECT_AUTHOR_EMAIL}>")
|
||||
message("*")
|
||||
option(ENTT_USE_LIBCPP "Use libc++ by adding -stdlib=libc++ flag if availbale." ON)
|
||||
option(ENTT_USE_SANITIZER "Enable sanitizers by adding -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined flags" OFF)
|
||||
|
||||
#
|
||||
# Compiler stuff
|
||||
#
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
if(NOT WIN32 AND ENTT_USE_LIBCPP)
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(CMakePushCheckState)
|
||||
|
||||
if(NOT MSVC)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DRELEASE")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g -DDEBUG")
|
||||
cmake_push_check_state()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
# it seems that -O3 ruins the performance when using clang ...
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2")
|
||||
else()
|
||||
# ... on the other side, GCC is incredibly comfortable with it.
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -stdlib=libc++")
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include<type_traits>
|
||||
int main() { return std::is_same_v<int, char>; }
|
||||
" ENTT_HAS_LIBCPP)
|
||||
|
||||
if(NOT ENTT_HAS_LIBCPP)
|
||||
message(VERBOSE "The option ENTT_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()
|
||||
endif()
|
||||
|
||||
#
|
||||
# Include EnTT
|
||||
# Add EnTT target
|
||||
#
|
||||
|
||||
include_directories(${entt_SOURCE_DIR}/src)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
add_library(EnTT INTERFACE)
|
||||
add_library(EnTT::EnTT ALIAS EnTT)
|
||||
|
||||
target_include_directories(
|
||||
EnTT
|
||||
INTERFACE
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
)
|
||||
|
||||
if(ENTT_USE_SANITIZER)
|
||||
target_compile_options(EnTT INTERFACE $<$<CONFIG:Debug>:-fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined>)
|
||||
target_link_libraries(EnTT INTERFACE $<$<CONFIG:Debug>:-fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined>)
|
||||
endif()
|
||||
|
||||
if(ENTT_HAS_LIBCPP)
|
||||
target_compile_options(EnTT BEFORE INTERFACE -stdlib=libc++)
|
||||
endif()
|
||||
|
||||
target_compile_features(EnTT INTERFACE cxx_std_17)
|
||||
|
||||
#
|
||||
# Install pkg-config file
|
||||
#
|
||||
|
||||
set(EnTT_PKGCONFIG ${CMAKE_CURRENT_BINARY_DIR}/entt.pc)
|
||||
|
||||
configure_file(
|
||||
${EnTT_SOURCE_DIR}/cmake/in/entt.pc.in
|
||||
${EnTT_PKGCONFIG}
|
||||
@ONLY
|
||||
)
|
||||
|
||||
install(
|
||||
FILES ${EnTT_PKGCONFIG}
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
||||
)
|
||||
|
||||
#
|
||||
# Install EnTT
|
||||
#
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
install(
|
||||
TARGETS EnTT
|
||||
EXPORT EnTTTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
|
||||
write_basic_package_version_file(
|
||||
EnTTConfigVersion.cmake
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
|
||||
configure_package_config_file(
|
||||
${EnTT_SOURCE_DIR}/cmake/in/EnTTConfig.cmake.in
|
||||
EnTTConfig.cmake
|
||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
|
||||
)
|
||||
|
||||
export(
|
||||
EXPORT EnTTTargets
|
||||
FILE ${CMAKE_CURRENT_BINARY_DIR}/EnTTTargets.cmake
|
||||
NAMESPACE EnTT::
|
||||
)
|
||||
|
||||
install(
|
||||
EXPORT EnTTTargets
|
||||
FILE EnTTTargets.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
|
||||
NAMESPACE EnTT::
|
||||
)
|
||||
|
||||
install(
|
||||
FILES
|
||||
${PROJECT_BINARY_DIR}/EnTTConfig.cmake
|
||||
${PROJECT_BINARY_DIR}/EnTTConfigVersion.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
|
||||
)
|
||||
|
||||
install(DIRECTORY src/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
||||
export(PACKAGE EnTT)
|
||||
|
||||
#
|
||||
# Tests
|
||||
#
|
||||
|
||||
option(BUILD_TESTING "Enable testing with ctest." ON)
|
||||
option(ENTT_BUILD_TESTING "Enable building tests." OFF)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
option(BUILD_BENCHMARK "Build benchmark." OFF)
|
||||
option(BUILD_MOD "Build mod example." OFF)
|
||||
|
||||
# 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)
|
||||
if(ENTT_BUILD_TESTING)
|
||||
option(ENTT_FIND_GTEST_PACKAGE "Enable finding gtest package." OFF)
|
||||
option(ENTT_BUILD_BENCHMARK "Build benchmark." OFF)
|
||||
option(ENTT_BUILD_EXAMPLE "Build examples." OFF)
|
||||
option(ENTT_BUILD_LIB "Build lib tests." OFF)
|
||||
option(ENTT_BUILD_SNAPSHOT "Build snapshot test with Cereal." OFF)
|
||||
|
||||
include(CTest)
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
@@ -89,10 +183,14 @@ endif()
|
||||
# Documentation
|
||||
#
|
||||
|
||||
find_package(Doxygen 1.8)
|
||||
option(ENTT_BUILD_DOCS "Enable building with documentation." OFF)
|
||||
|
||||
if(DOXYGEN_FOUND)
|
||||
add_subdirectory(docs)
|
||||
if(ENTT_BUILD_DOCS)
|
||||
find_package(Doxygen 1.8)
|
||||
|
||||
if(DOXYGEN_FOUND)
|
||||
add_subdirectory(docs)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#
|
||||
@@ -100,11 +198,16 @@ endif()
|
||||
#
|
||||
|
||||
add_custom_target(
|
||||
entt_aob
|
||||
aob
|
||||
SOURCES
|
||||
appveyor.yml
|
||||
.github/workflows/build.yml
|
||||
.github/workflows/coverage.yml
|
||||
.github/workflows/deploy.yml
|
||||
.github/workflows/sanitizer.yml
|
||||
.github/FUNDING.yml
|
||||
AUTHORS
|
||||
CONTRIBUTING.md
|
||||
LICENSE
|
||||
README.md
|
||||
.travis.yml
|
||||
TODO
|
||||
)
|
||||
|
||||
43
CONTRIBUTING.md
Normal file
43
CONTRIBUTING.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Contributing
|
||||
|
||||
First of all, thank you very much for taking the time to contribute to the
|
||||
`EnTT` framework.<br/>
|
||||
How to do it mostly depends on the type of contribution:
|
||||
|
||||
* If you have a question, **please** ensure there isn't already an answer for
|
||||
you by searching on GitHub under
|
||||
[issues](https://github.com/skypjack/entt/issues). Do not forget to search
|
||||
also through the closed ones. If you are unable to find a proper answer, feel
|
||||
free to [open a new issue](https://github.com/skypjack/entt/issues/new).
|
||||
Usually, questions are marked as such and closed in a few days.
|
||||
|
||||
* If you want to fix a typo in the inline documentation or in the README file,
|
||||
if you want to add some new sections or if you want to help me with the
|
||||
language by reviewing what I wrote so far (I'm not a native speaker after
|
||||
all), **please** open a new
|
||||
[pull request](https://github.com/skypjack/entt/pulls) with your changes.
|
||||
|
||||
* If you found a bug, **please** ensure there isn't already an answer for you by
|
||||
searching on GitHub under [issues](https://github.com/skypjack/entt/issues).
|
||||
If you are unable to find an open issue addressing the problem, feel free to
|
||||
[open a new one](https://github.com/skypjack/entt/issues/new). **Please**, do
|
||||
not forget to carefully describe how to reproduce the problem, then add all
|
||||
the information about the system on which you are experiencing it and point
|
||||
out the version of `EnTT` you are using (tag or commit).
|
||||
|
||||
* If you found a bug and you wrote a patch to fix it, open a new
|
||||
[pull request](https://github.com/skypjack/entt/pulls) with your code.
|
||||
**Please**, add some tests to avoid regressions in future if possible, it
|
||||
would be really appreciated. Note that the `EnTT` framework has a
|
||||
[coverage at 100%](https://coveralls.io/github/skypjack/entt?branch=master)
|
||||
(at least it was at 100% at the time I wrote this file) and this is the reason
|
||||
for which you can be confident with using it in a production environment.
|
||||
|
||||
* If you want to propose a new feature and you know how to code it, **please**
|
||||
do not issue directly a pull request. Before to do it,
|
||||
[create a new issue](https://github.com/skypjack/entt/issues/new) to discuss
|
||||
your proposal. Other users could be interested in your idea and the discussion
|
||||
that will follow can refine it and therefore give us a better solution.
|
||||
|
||||
* If you want to request a new feature, I'm available for hiring. Take a look at
|
||||
[my profile](https://github.com/skypjack) and feel free to write me.
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Michele Caini
|
||||
Copyright (c) 2017-2021 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
|
||||
|
||||
30
TODO
Normal file
30
TODO
Normal file
@@ -0,0 +1,30 @@
|
||||
* 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
|
||||
* work stealing job system (see #100) + mt scheduler based on const awareness for types
|
||||
* allow to replace std:: with custom implementations
|
||||
* add examples (and credits) from @alanjfs :)
|
||||
* custom pools example (multi instance, tables, enable/disable, and so on...)
|
||||
|
||||
WIP:
|
||||
* HP: scheduler, use any (or poly?) instead of unique_ptr
|
||||
* HP: resource, forward the id to the loader from the cache and if constexpr the call to load, update doc and describe customization points
|
||||
* HP: make it possible to create views of the type `view<T, T>`, add get by index and such, allow to register custom pools by name with the registry
|
||||
* HP: add user data to type_info
|
||||
* HP: make pools available (registry/view/group), review operator| for views
|
||||
* HP: any_vector for context variables
|
||||
* HP: make const registry::view thread safe, switch to a view<T...>{registry} model (long term goal)
|
||||
* HP: weak reference wrapper example with custom storage
|
||||
* HP: paginate pools
|
||||
* HP: headless (sparse set only) view
|
||||
* HP: write documentation for custom storages and views!!
|
||||
* HP: registry: use a poly object for pools, no more pool_data type.
|
||||
* HP: make runtime views use opaque storage and therefore return also elements.
|
||||
* HP: add exclude-only views to combine with packs
|
||||
* HP: entity-aware observer, add observer functions aside observer class
|
||||
* HP: any and the like: remove constructor that accepts reference wrapper, allow only in-place T&?
|
||||
* deprecate non-owning groups in favor of owning views and view packs, introduce lazy owning views
|
||||
* pagination doesn't work nicely across boundaries probably, give it a look. RO operations are fine, adding components maybe not.
|
||||
* snapshot: support for range-based archives
|
||||
* page size 0 -> page less mode
|
||||
* add example: 64 bit ids with 32 bits reserved for users' purposes
|
||||
22
appveyor.yml
22
appveyor.yml
@@ -1,22 +0,0 @@
|
||||
# can use variables like {build} and {branch}
|
||||
version: 1.0.{build}
|
||||
|
||||
image: Visual Studio 2017
|
||||
|
||||
environment:
|
||||
BUILD_DIR: "%APPVEYOR_BUILD_FOLDER%\\build"
|
||||
|
||||
platform:
|
||||
- Win32
|
||||
|
||||
configuration:
|
||||
- Release
|
||||
|
||||
before_build:
|
||||
- cd %BUILD_DIR%
|
||||
- cmake .. -DCMAKE_CXX_FLAGS=/D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING -G"Visual Studio 15 2017"
|
||||
|
||||
build:
|
||||
parallel: true
|
||||
project: build/entt.sln
|
||||
verbosity: minimal
|
||||
5
cmake/in/EnTTConfig.cmake.in
Normal file
5
cmake/in/EnTTConfig.cmake.in
Normal file
@@ -0,0 +1,5 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
set(EnTT_VERSION "@PROJECT_VERSION@")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/EnTTTargets.cmake")
|
||||
check_required_components("@PROJECT_NAME@")
|
||||
@@ -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 ""
|
||||
)
|
||||
8
cmake/in/entt.pc.in
Normal file
8
cmake/in/entt.pc.in
Normal file
@@ -0,0 +1,8 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||
|
||||
Name: EnTT
|
||||
Description: Gaming meets modern C++
|
||||
Url: https://github.com/skypjack/entt
|
||||
Version: @ENTT_VERSION@
|
||||
Cflags: -I${includedir}
|
||||
@@ -1,19 +0,0 @@
|
||||
project(googletest-download NONE)
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
include(ExternalProject)
|
||||
|
||||
ExternalProject_Add(
|
||||
googletest
|
||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||
GIT_TAG release-1.8.0
|
||||
DOWNLOAD_DIR ${GOOGLETEST_DEPS_DIR}
|
||||
TMP_DIR ${GOOGLETEST_DEPS_DIR}/tmp
|
||||
STAMP_DIR ${GOOGLETEST_DEPS_DIR}/stamp
|
||||
SOURCE_DIR ${GOOGLETEST_DEPS_DIR}/src
|
||||
BINARY_DIR ${GOOGLETEST_DEPS_DIR}/build
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
TEST_COMMAND ""
|
||||
)
|
||||
37
conan/build.py
Normal file
37
conan/build.py
Normal file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from cpt.packager import ConanMultiPackager
|
||||
import os
|
||||
|
||||
if __name__ == "__main__":
|
||||
username = os.getenv("GITHUB_ACTOR")
|
||||
tag_version = os.getenv("GITHUB_REF")
|
||||
tag_package = os.getenv("GITHUB_REPOSITORY")
|
||||
login_username = os.getenv("CONAN_LOGIN_USERNAME")
|
||||
package_version = tag_version.replace("refs/tags/v", "")
|
||||
package_name = tag_package.replace("skypjack/", "")
|
||||
reference = "{}/{}".format(package_name, package_version)
|
||||
channel = os.getenv("CONAN_CHANNEL", "stable")
|
||||
upload = os.getenv("CONAN_UPLOAD")
|
||||
stable_branch_pattern = os.getenv("CONAN_STABLE_BRANCH_PATTERN", r"v\d+\.\d+\.\d+.*")
|
||||
test_folder = os.getenv("CPT_TEST_FOLDER", os.path.join("conan", "test_package"))
|
||||
upload_only_when_stable = os.getenv("CONAN_UPLOAD_ONLY_WHEN_STABLE", True)
|
||||
disable_shared = os.getenv("CONAN_DISABLE_SHARED_BUILD", "False")
|
||||
|
||||
builder = ConanMultiPackager(username=username,
|
||||
reference=reference,
|
||||
channel=channel,
|
||||
login_username=login_username,
|
||||
upload=upload,
|
||||
stable_branch_pattern=stable_branch_pattern,
|
||||
upload_only_when_stable=upload_only_when_stable,
|
||||
test_folder=test_folder)
|
||||
builder.add()
|
||||
|
||||
filtered_builds = []
|
||||
for settings, options, env_vars, build_requires, reference in builder.items:
|
||||
if disable_shared == "False" or not options["{}:shared".format(package_name)]:
|
||||
filtered_builds.append([settings, options, env_vars, build_requires])
|
||||
builder.builds = filtered_builds
|
||||
|
||||
builder.run()
|
||||
7
conan/ci/build.sh
Normal file
7
conan/ci/build.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
conan user
|
||||
python conan/build.py
|
||||
6
conan/ci/install.sh
Normal file
6
conan/ci/install.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
pip install -U conan_package_tools conan
|
||||
13
conan/test_package/CMakeLists.txt
Normal file
13
conan/test_package/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
cmake_minimum_required(VERSION 3.7.2)
|
||||
project(test_package)
|
||||
|
||||
set(CMAKE_VERBOSE_MAKEFILE TRUE)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
|
||||
conan_basic_setup()
|
||||
|
||||
add_executable(${PROJECT_NAME} test_package.cpp)
|
||||
target_link_libraries(${PROJECT_NAME} ${CONAN_LIBS})
|
||||
19
conan/test_package/conanfile.py
Normal file
19
conan/test_package/conanfile.py
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from conans import ConanFile, CMake
|
||||
import os
|
||||
|
||||
|
||||
class TestPackageConan(ConanFile):
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
generators = "cmake"
|
||||
|
||||
def build(self):
|
||||
cmake = CMake(self)
|
||||
cmake.configure()
|
||||
cmake.build()
|
||||
|
||||
def test(self):
|
||||
bin_path = os.path.join("bin", "test_package")
|
||||
self.run(bin_path, run_environment=True)
|
||||
56
conan/test_package/test_package.cpp
Normal file
56
conan/test_package/test_package.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <entt/entt.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
struct position {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
struct velocity {
|
||||
float dx;
|
||||
float dy;
|
||||
};
|
||||
|
||||
void update(entt::registry ®istry) {
|
||||
auto view = registry.view<position, velocity>();
|
||||
|
||||
for(auto entity: view) {
|
||||
// gets only the components that are going to be used ...
|
||||
|
||||
auto &vel = view.get<velocity>(entity);
|
||||
|
||||
vel.dx = 0.;
|
||||
vel.dy = 0.;
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
void update(std::uint64_t dt, entt::registry ®istry) {
|
||||
registry.view<position, velocity>().each([dt](auto &pos, auto &vel) {
|
||||
// gets all the components of the view at once ...
|
||||
|
||||
pos.x += vel.dx * dt;
|
||||
pos.y += vel.dy * dt;
|
||||
|
||||
// ...
|
||||
});
|
||||
}
|
||||
|
||||
int main() {
|
||||
entt::registry registry;
|
||||
std::uint64_t dt = 16;
|
||||
|
||||
for(auto i = 0; i < 10; ++i) {
|
||||
auto entity = registry.create();
|
||||
registry.emplace<position>(entity, i * 1.f, i * 1.f);
|
||||
if(i % 2 == 0) { registry.emplace<velocity>(entity, i * .1f, i * .1f); }
|
||||
}
|
||||
|
||||
update(dt, registry);
|
||||
update(registry);
|
||||
|
||||
// ...
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
27
conanfile.py
Normal file
27
conanfile.py
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from conans import ConanFile
|
||||
|
||||
|
||||
class EnttConan(ConanFile):
|
||||
name = "entt"
|
||||
description = "Gaming meets modern C++ - a fast and reliable entity-component system (ECS) and much more "
|
||||
topics = ("conan," "entt", "gaming", "entity", "ecs")
|
||||
url = "https://github.com/skypjack/entt"
|
||||
homepage = url
|
||||
author = "Michele Caini <michele.caini@gmail.com>"
|
||||
license = "MIT"
|
||||
exports = ["LICENSE"]
|
||||
exports_sources = ["src/*"]
|
||||
no_copy_source = True
|
||||
|
||||
def package(self):
|
||||
self.copy(pattern="LICENSE", dst="licenses")
|
||||
self.copy(pattern="*", dst="include", src="src", keep_path=True)
|
||||
|
||||
def package_info(self):
|
||||
if not self.in_local_cache:
|
||||
self.cpp_info.includedirs = ["src"]
|
||||
|
||||
def package_id(self):
|
||||
self.info.header_only()
|
||||
2
deps/.gitignore
vendored
2
deps/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -2,20 +2,35 @@
|
||||
# Doxygen configuration (documentation)
|
||||
#
|
||||
|
||||
set(TARGET_DOCS docs)
|
||||
|
||||
set(DOXY_SOURCE_DIRECTORY ${entt_SOURCE_DIR}/src)
|
||||
set(DOXY_DEPS_DIRECTORY ${EnTT_SOURCE_DIR}/deps)
|
||||
set(DOXY_SOURCE_DIRECTORY ${EnTT_SOURCE_DIR}/src)
|
||||
set(DOXY_DOCS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(DOXY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
configure_file(doxy.in doxy.cfg @ONLY)
|
||||
|
||||
add_custom_target(
|
||||
${TARGET_DOCS}
|
||||
docs ALL
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.cfg
|
||||
WORKING_DIRECTORY ${entt_SOURCE_DIR}
|
||||
WORKING_DIRECTORY ${EnTT_SOURCE_DIR}
|
||||
VERBATIM
|
||||
SOURCES doxy.in
|
||||
SOURCES
|
||||
dox/extra.dox
|
||||
md/config.md
|
||||
md/core.md
|
||||
md/entity.md
|
||||
md/faq.md
|
||||
md/lib.md
|
||||
md/links.md
|
||||
md/locator.md
|
||||
md/meta.md
|
||||
md/poly.md
|
||||
md/process.md
|
||||
md/reference.md
|
||||
md/resource.md
|
||||
md/signal.md
|
||||
md/unreal.md
|
||||
doxy.in
|
||||
)
|
||||
|
||||
install(
|
||||
|
||||
395
docs/LICENSE
395
docs/LICENSE
@@ -1,395 +0,0 @@
|
||||
Attribution 4.0 International
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||
does not provide legal services or legal advice. Distribution of
|
||||
Creative Commons public licenses does not create a lawyer-client or
|
||||
other relationship. Creative Commons makes its licenses and related
|
||||
information available on an "as-is" basis. Creative Commons gives no
|
||||
warranties regarding its licenses, any material licensed under their
|
||||
terms and conditions, or any related information. Creative Commons
|
||||
disclaims all liability for damages resulting from their use to the
|
||||
fullest extent possible.
|
||||
|
||||
Using Creative Commons Public Licenses
|
||||
|
||||
Creative Commons public licenses provide a standard set of terms and
|
||||
conditions that creators and other rights holders may use to share
|
||||
original works of authorship and other material subject to copyright
|
||||
and certain other rights specified in the public license below. The
|
||||
following considerations are for informational purposes only, are not
|
||||
exhaustive, and do not form part of our licenses.
|
||||
|
||||
Considerations for licensors: Our public licenses are
|
||||
intended for use by those authorized to give the public
|
||||
permission to use material in ways otherwise restricted by
|
||||
copyright and certain other rights. Our licenses are
|
||||
irrevocable. Licensors should read and understand the terms
|
||||
and conditions of the license they choose before applying it.
|
||||
Licensors should also secure all rights necessary before
|
||||
applying our licenses so that the public can reuse the
|
||||
material as expected. Licensors should clearly mark any
|
||||
material not subject to the license. This includes other CC-
|
||||
licensed material, or material used under an exception or
|
||||
limitation to copyright. More considerations for licensors:
|
||||
wiki.creativecommons.org/Considerations_for_licensors
|
||||
|
||||
Considerations for the public: By using one of our public
|
||||
licenses, a licensor grants the public permission to use the
|
||||
licensed material under specified terms and conditions. If
|
||||
the licensor's permission is not necessary for any reason--for
|
||||
example, because of any applicable exception or limitation to
|
||||
copyright--then that use is not regulated by the license. Our
|
||||
licenses grant only permissions under copyright and certain
|
||||
other rights that a licensor has authority to grant. Use of
|
||||
the licensed material may still be restricted for other
|
||||
reasons, including because others have copyright or other
|
||||
rights in the material. A licensor may make special requests,
|
||||
such as asking that all changes be marked or described.
|
||||
Although not required by our licenses, you are encouraged to
|
||||
respect those requests where reasonable. More_considerations
|
||||
for the public:
|
||||
wiki.creativecommons.org/Considerations_for_licensees
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Attribution 4.0 International Public License
|
||||
|
||||
By exercising the Licensed Rights (defined below), You accept and agree
|
||||
to be bound by the terms and conditions of this Creative Commons
|
||||
Attribution 4.0 International Public License ("Public License"). To the
|
||||
extent this Public License may be interpreted as a contract, You are
|
||||
granted the Licensed Rights in consideration of Your acceptance of
|
||||
these terms and conditions, and the Licensor grants You such rights in
|
||||
consideration of benefits the Licensor receives from making the
|
||||
Licensed Material available under these terms and conditions.
|
||||
|
||||
|
||||
Section 1 -- Definitions.
|
||||
|
||||
a. Adapted Material means material subject to Copyright and Similar
|
||||
Rights that is derived from or based upon the Licensed Material
|
||||
and in which the Licensed Material is translated, altered,
|
||||
arranged, transformed, or otherwise modified in a manner requiring
|
||||
permission under the Copyright and Similar Rights held by the
|
||||
Licensor. For purposes of this Public License, where the Licensed
|
||||
Material is a musical work, performance, or sound recording,
|
||||
Adapted Material is always produced where the Licensed Material is
|
||||
synched in timed relation with a moving image.
|
||||
|
||||
b. Adapter's License means the license You apply to Your Copyright
|
||||
and Similar Rights in Your contributions to Adapted Material in
|
||||
accordance with the terms and conditions of this Public License.
|
||||
|
||||
c. Copyright and Similar Rights means copyright and/or similar rights
|
||||
closely related to copyright including, without limitation,
|
||||
performance, broadcast, sound recording, and Sui Generis Database
|
||||
Rights, without regard to how the rights are labeled or
|
||||
categorized. For purposes of this Public License, the rights
|
||||
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||
Rights.
|
||||
|
||||
d. Effective Technological Measures means those measures that, in the
|
||||
absence of proper authority, may not be circumvented under laws
|
||||
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||
Treaty adopted on December 20, 1996, and/or similar international
|
||||
agreements.
|
||||
|
||||
e. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||
any other exception or limitation to Copyright and Similar Rights
|
||||
that applies to Your use of the Licensed Material.
|
||||
|
||||
f. Licensed Material means the artistic or literary work, database,
|
||||
or other material to which the Licensor applied this Public
|
||||
License.
|
||||
|
||||
g. Licensed Rights means the rights granted to You subject to the
|
||||
terms and conditions of this Public License, which are limited to
|
||||
all Copyright and Similar Rights that apply to Your use of the
|
||||
Licensed Material and that the Licensor has authority to license.
|
||||
|
||||
h. Licensor means the individual(s) or entity(ies) granting rights
|
||||
under this Public License.
|
||||
|
||||
i. Share means to provide material to the public by any means or
|
||||
process that requires permission under the Licensed Rights, such
|
||||
as reproduction, public display, public performance, distribution,
|
||||
dissemination, communication, or importation, and to make material
|
||||
available to the public including in ways that members of the
|
||||
public may access the material from a place and at a time
|
||||
individually chosen by them.
|
||||
|
||||
j. Sui Generis Database Rights means rights other than copyright
|
||||
resulting from Directive 96/9/EC of the European Parliament and of
|
||||
the Council of 11 March 1996 on the legal protection of databases,
|
||||
as amended and/or succeeded, as well as other essentially
|
||||
equivalent rights anywhere in the world.
|
||||
|
||||
k. You means the individual or entity exercising the Licensed Rights
|
||||
under this Public License. Your has a corresponding meaning.
|
||||
|
||||
|
||||
Section 2 -- Scope.
|
||||
|
||||
a. License grant.
|
||||
|
||||
1. Subject to the terms and conditions of this Public License,
|
||||
the Licensor hereby grants You a worldwide, royalty-free,
|
||||
non-sublicensable, non-exclusive, irrevocable license to
|
||||
exercise the Licensed Rights in the Licensed Material to:
|
||||
|
||||
a. reproduce and Share the Licensed Material, in whole or
|
||||
in part; and
|
||||
|
||||
b. produce, reproduce, and Share Adapted Material.
|
||||
|
||||
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||
Exceptions and Limitations apply to Your use, this Public
|
||||
License does not apply, and You do not need to comply with
|
||||
its terms and conditions.
|
||||
|
||||
3. Term. The term of this Public License is specified in Section
|
||||
6(a).
|
||||
|
||||
4. Media and formats; technical modifications allowed. The
|
||||
Licensor authorizes You to exercise the Licensed Rights in
|
||||
all media and formats whether now known or hereafter created,
|
||||
and to make technical modifications necessary to do so. The
|
||||
Licensor waives and/or agrees not to assert any right or
|
||||
authority to forbid You from making technical modifications
|
||||
necessary to exercise the Licensed Rights, including
|
||||
technical modifications necessary to circumvent Effective
|
||||
Technological Measures. For purposes of this Public License,
|
||||
simply making modifications authorized by this Section 2(a)
|
||||
(4) never produces Adapted Material.
|
||||
|
||||
5. Downstream recipients.
|
||||
|
||||
a. Offer from the Licensor -- Licensed Material. Every
|
||||
recipient of the Licensed Material automatically
|
||||
receives an offer from the Licensor to exercise the
|
||||
Licensed Rights under the terms and conditions of this
|
||||
Public License.
|
||||
|
||||
b. No downstream restrictions. You may not offer or impose
|
||||
any additional or different terms or conditions on, or
|
||||
apply any Effective Technological Measures to, the
|
||||
Licensed Material if doing so restricts exercise of the
|
||||
Licensed Rights by any recipient of the Licensed
|
||||
Material.
|
||||
|
||||
6. No endorsement. Nothing in this Public License constitutes or
|
||||
may be construed as permission to assert or imply that You
|
||||
are, or that Your use of the Licensed Material is, connected
|
||||
with, or sponsored, endorsed, or granted official status by,
|
||||
the Licensor or others designated to receive attribution as
|
||||
provided in Section 3(a)(1)(A)(i).
|
||||
|
||||
b. Other rights.
|
||||
|
||||
1. Moral rights, such as the right of integrity, are not
|
||||
licensed under this Public License, nor are publicity,
|
||||
privacy, and/or other similar personality rights; however, to
|
||||
the extent possible, the Licensor waives and/or agrees not to
|
||||
assert any such rights held by the Licensor to the limited
|
||||
extent necessary to allow You to exercise the Licensed
|
||||
Rights, but not otherwise.
|
||||
|
||||
2. Patent and trademark rights are not licensed under this
|
||||
Public License.
|
||||
|
||||
3. To the extent possible, the Licensor waives any right to
|
||||
collect royalties from You for the exercise of the Licensed
|
||||
Rights, whether directly or through a collecting society
|
||||
under any voluntary or waivable statutory or compulsory
|
||||
licensing scheme. In all other cases the Licensor expressly
|
||||
reserves any right to collect such royalties.
|
||||
|
||||
|
||||
Section 3 -- License Conditions.
|
||||
|
||||
Your exercise of the Licensed Rights is expressly made subject to the
|
||||
following conditions.
|
||||
|
||||
a. Attribution.
|
||||
|
||||
1. If You Share the Licensed Material (including in modified
|
||||
form), You must:
|
||||
|
||||
a. retain the following if it is supplied by the Licensor
|
||||
with the Licensed Material:
|
||||
|
||||
i. identification of the creator(s) of the Licensed
|
||||
Material and any others designated to receive
|
||||
attribution, in any reasonable manner requested by
|
||||
the Licensor (including by pseudonym if
|
||||
designated);
|
||||
|
||||
ii. a copyright notice;
|
||||
|
||||
iii. a notice that refers to this Public License;
|
||||
|
||||
iv. a notice that refers to the disclaimer of
|
||||
warranties;
|
||||
|
||||
v. a URI or hyperlink to the Licensed Material to the
|
||||
extent reasonably practicable;
|
||||
|
||||
b. indicate if You modified the Licensed Material and
|
||||
retain an indication of any previous modifications; and
|
||||
|
||||
c. indicate the Licensed Material is licensed under this
|
||||
Public License, and include the text of, or the URI or
|
||||
hyperlink to, this Public License.
|
||||
|
||||
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||
reasonable manner based on the medium, means, and context in
|
||||
which You Share the Licensed Material. For example, it may be
|
||||
reasonable to satisfy the conditions by providing a URI or
|
||||
hyperlink to a resource that includes the required
|
||||
information.
|
||||
|
||||
3. If requested by the Licensor, You must remove any of the
|
||||
information required by Section 3(a)(1)(A) to the extent
|
||||
reasonably practicable.
|
||||
|
||||
4. If You Share Adapted Material You produce, the Adapter's
|
||||
License You apply must not prevent recipients of the Adapted
|
||||
Material from complying with this Public License.
|
||||
|
||||
|
||||
Section 4 -- Sui Generis Database Rights.
|
||||
|
||||
Where the Licensed Rights include Sui Generis Database Rights that
|
||||
apply to Your use of the Licensed Material:
|
||||
|
||||
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||
to extract, reuse, reproduce, and Share all or a substantial
|
||||
portion of the contents of the database;
|
||||
|
||||
b. if You include all or a substantial portion of the database
|
||||
contents in a database in which You have Sui Generis Database
|
||||
Rights, then the database in which You have Sui Generis Database
|
||||
Rights (but not its individual contents) is Adapted Material; and
|
||||
|
||||
c. You must comply with the conditions in Section 3(a) if You Share
|
||||
all or a substantial portion of the contents of the database.
|
||||
|
||||
For the avoidance of doubt, this Section 4 supplements and does not
|
||||
replace Your obligations under this Public License where the Licensed
|
||||
Rights include other Copyright and Similar Rights.
|
||||
|
||||
|
||||
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||
|
||||
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||
|
||||
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||
|
||||
c. The disclaimer of warranties and limitation of liability provided
|
||||
above shall be interpreted in a manner that, to the extent
|
||||
possible, most closely approximates an absolute disclaimer and
|
||||
waiver of all liability.
|
||||
|
||||
|
||||
Section 6 -- Term and Termination.
|
||||
|
||||
a. This Public License applies for the term of the Copyright and
|
||||
Similar Rights licensed here. However, if You fail to comply with
|
||||
this Public License, then Your rights under this Public License
|
||||
terminate automatically.
|
||||
|
||||
b. Where Your right to use the Licensed Material has terminated under
|
||||
Section 6(a), it reinstates:
|
||||
|
||||
1. automatically as of the date the violation is cured, provided
|
||||
it is cured within 30 days of Your discovery of the
|
||||
violation; or
|
||||
|
||||
2. upon express reinstatement by the Licensor.
|
||||
|
||||
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||
right the Licensor may have to seek remedies for Your violations
|
||||
of this Public License.
|
||||
|
||||
c. For the avoidance of doubt, the Licensor may also offer the
|
||||
Licensed Material under separate terms or conditions or stop
|
||||
distributing the Licensed Material at any time; however, doing so
|
||||
will not terminate this Public License.
|
||||
|
||||
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||
License.
|
||||
|
||||
|
||||
Section 7 -- Other Terms and Conditions.
|
||||
|
||||
a. The Licensor shall not be bound by any additional or different
|
||||
terms or conditions communicated by You unless expressly agreed.
|
||||
|
||||
b. Any arrangements, understandings, or agreements regarding the
|
||||
Licensed Material not stated herein are separate from and
|
||||
independent of the terms and conditions of this Public License.
|
||||
|
||||
|
||||
Section 8 -- Interpretation.
|
||||
|
||||
a. For the avoidance of doubt, this Public License does not, and
|
||||
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||
conditions on any use of the Licensed Material that could lawfully
|
||||
be made without permission under this Public License.
|
||||
|
||||
b. To the extent possible, if any provision of this Public License is
|
||||
deemed unenforceable, it shall be automatically reformed to the
|
||||
minimum extent necessary to make it enforceable. If the provision
|
||||
cannot be reformed, it shall be severed from this Public License
|
||||
without affecting the enforceability of the remaining terms and
|
||||
conditions.
|
||||
|
||||
c. No term or condition of this Public License will be waived and no
|
||||
failure to comply consented to unless expressly agreed to by the
|
||||
Licensor.
|
||||
|
||||
d. Nothing in this Public License constitutes or may be interpreted
|
||||
as a limitation upon, or waiver of, any privileges and immunities
|
||||
that apply to the Licensor or You, including from the legal
|
||||
processes of any jurisdiction or authority.
|
||||
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons is not a party to its public
|
||||
licenses. Notwithstanding, Creative Commons may elect to apply one of
|
||||
its public licenses to material it publishes and in those instances
|
||||
will be considered the “Licensor.” The text of the Creative Commons
|
||||
public licenses is dedicated to the public domain under the CC0 Public
|
||||
Domain Dedication. Except for the limited purpose of indicating that
|
||||
material is shared under a Creative Commons public license or as
|
||||
otherwise permitted by the Creative Commons policies published at
|
||||
creativecommons.org/policies, Creative Commons does not authorize the
|
||||
use of the trademark "Creative Commons" or any other trademark or logo
|
||||
of Creative Commons without its prior written consent including,
|
||||
without limitation, in connection with any unauthorized modifications
|
||||
to any of its public licenses or any other arrangements,
|
||||
understandings, or agreements concerning use of licensed material. For
|
||||
the avoidance of doubt, this paragraph does not form part of the
|
||||
public licenses.
|
||||
|
||||
Creative Commons may be contacted at creativecommons.org.
|
||||
307
docs/doxy.in
307
docs/doxy.in
@@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.8.13
|
||||
# Doxyfile 1.8.20
|
||||
|
||||
# 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
|
||||
@@ -199,6 +217,14 @@ QT_AUTOBRIEF = NO
|
||||
|
||||
MULTILINE_CPP_IS_BRIEF = NO
|
||||
|
||||
# By default Python docstrings are displayed as preformatted text and doxygen's
|
||||
# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
|
||||
# doxygen's special commands can be used and the contents of the docstring
|
||||
# documentation blocks is shown as doxygen documentation.
|
||||
# The default value is: YES.
|
||||
|
||||
PYTHON_DOCSTRING = YES
|
||||
|
||||
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
|
||||
# documentation from any documented member that it re-implements.
|
||||
# The default value is: YES.
|
||||
@@ -226,16 +252,15 @@ 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 =
|
||||
|
||||
# This tag can be used to specify a number of word-keyword mappings (TCL only).
|
||||
# A mapping has the form "name=value". For example adding "class=itcl::class"
|
||||
# will allow you to use the command class in the itcl::class meaning.
|
||||
|
||||
TCL_SUBST =
|
||||
|
||||
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
|
||||
# only. Doxygen will then generate output that is more tailored for C. For
|
||||
# instance, some of the names that are used will be different. The list of all
|
||||
@@ -264,17 +289,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.
|
||||
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
|
||||
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
|
||||
# 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). 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 +319,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 +331,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 +361,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.
|
||||
@@ -413,6 +447,19 @@ TYPEDEF_HIDES_STRUCT = NO
|
||||
|
||||
LOOKUP_CACHE_SIZE = 0
|
||||
|
||||
# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use
|
||||
# during processing. When set to 0 doxygen will based this on the number of
|
||||
# cores available in the system. You can set it explicitly to a value larger
|
||||
# than 0 to get more control over the balance between CPU load and processing
|
||||
# speed. At this moment only the input processing can be done using multiple
|
||||
# threads. Since this is still an experimental feature the default is set to 1,
|
||||
# which efficively disables parallel processing. Please report any issues you
|
||||
# encounter. Generating dot graphs in parallel is controlled by the
|
||||
# DOT_NUM_THREADS setting.
|
||||
# Minimum value: 0, maximum value: 32, default value: 1.
|
||||
|
||||
NUM_PROC_THREADS = 1
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Build related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -433,6 +480,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.
|
||||
@@ -487,8 +540,8 @@ HIDE_UNDOC_MEMBERS = NO
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
|
||||
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
|
||||
# (class|struct|union) declarations. If set to NO, these declarations will be
|
||||
# included in the documentation.
|
||||
# declarations. If set to NO, these declarations will be included in the
|
||||
# documentation.
|
||||
# The default value is: NO.
|
||||
|
||||
HIDE_FRIEND_COMPOUNDS = NO
|
||||
@@ -511,7 +564,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) and Mac users are advised to set this option to NO.
|
||||
# The default value is: system dependent.
|
||||
|
||||
CASE_SENSE_NAMES = YES
|
||||
@@ -698,7 +751,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 +796,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
|
||||
@@ -780,12 +834,14 @@ WARN_LOGFILE =
|
||||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = @DOXY_SOURCE_DIRECTORY@ @DOXY_DOCS_DIRECTORY@ @PROJECT_SOURCE_DIR@/README.md
|
||||
INPUT = @DOXY_SOURCE_DIRECTORY@ \
|
||||
@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.
|
||||
|
||||
@@ -802,12 +858,12 @@ INPUT_ENCODING = UTF-8
|
||||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
|
||||
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
|
||||
# *.hh, *.hxx, *.hpp, *.h++, *.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.
|
||||
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
|
||||
# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
|
||||
# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
|
||||
# *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
|
||||
FILE_PATTERNS = *.cpp \
|
||||
*.c++ \
|
||||
*.h \
|
||||
FILE_PATTERNS = *.h \
|
||||
*.hpp \
|
||||
*.md \
|
||||
*.dox
|
||||
@@ -825,7 +881,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 +1019,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 +1051,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 +1084,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 +1097,19 @@ CLANG_ASSISTED_PARSING = NO
|
||||
|
||||
CLANG_OPTIONS =
|
||||
|
||||
# If clang assisted parsing is enabled you can provide the clang parser with the
|
||||
# path to the directory containing a file called compile_commands.json. This
|
||||
# file is the compilation database (see:
|
||||
# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the
|
||||
# options used when the source 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. Any options specified with CLANG_OPTIONS
|
||||
# will be added as well.
|
||||
# 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 +1228,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 +1264,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 +1298,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 +1343,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
|
||||
@@ -1294,7 +1374,7 @@ CHM_FILE =
|
||||
HHC_LOCATION =
|
||||
|
||||
# The GENERATE_CHI flag controls if a separate .chi index file is generated
|
||||
# (YES) or that it should be included in the master .chm file (NO).
|
||||
# (YES) or that it should be included in the main .chm file (NO).
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
|
||||
|
||||
@@ -1339,7 +1419,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 +1427,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 +1436,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 +1444,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 +1452,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 =
|
||||
@@ -1456,6 +1536,17 @@ TREEVIEW_WIDTH = 250
|
||||
|
||||
EXT_LINKS_IN_WINDOW = NO
|
||||
|
||||
# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
|
||||
# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
|
||||
# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
|
||||
# the HTML output. These images will generally look nicer at scaled resolutions.
|
||||
# Possible values are: png (the default) and svg (looks nicer but requires the
|
||||
# pdf2svg or inkscape tool).
|
||||
# The default value is: png.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_FORMULA_FORMAT = png
|
||||
|
||||
# Use this tag to change the font size of LaTeX formulas included as images in
|
||||
# the HTML documentation. When you change the font size after a successful
|
||||
# doxygen run you need to manually remove any form_*.png images from the HTML
|
||||
@@ -1465,7 +1556,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.
|
||||
#
|
||||
@@ -1476,8 +1567,14 @@ FORMULA_FONTSIZE = 10
|
||||
|
||||
FORMULA_TRANSPARENT = YES
|
||||
|
||||
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
|
||||
# to create new LaTeX commands to be used in formulas as building blocks. See
|
||||
# the section "Including formulas" for details.
|
||||
|
||||
FORMULA_MACROFILE =
|
||||
|
||||
# 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 +1601,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://cdn.jsdelivr.net/npm/mathjax@2.
|
||||
# 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
|
||||
@@ -1547,7 +1644,7 @@ MATHJAX_CODEFILE =
|
||||
SEARCHENGINE = YES
|
||||
|
||||
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
|
||||
# implemented using a web server instead of a web client using Javascript. There
|
||||
# implemented using a web server instead of a web client using JavaScript. There
|
||||
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
|
||||
# setting. When disabled, doxygen will generate a PHP script for searching and
|
||||
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
|
||||
@@ -1566,7 +1663,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 +1676,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 +1728,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.
|
||||
@@ -1730,9 +1841,11 @@ LATEX_EXTRA_FILES =
|
||||
|
||||
PDF_HYPERLINKS = YES
|
||||
|
||||
# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
|
||||
# the PDF file directly from the LaTeX files. Set this option to YES, to get a
|
||||
# higher quality PDF documentation.
|
||||
# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
|
||||
# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
|
||||
# files. Set this option to YES, to get a higher quality PDF documentation.
|
||||
#
|
||||
# See also section LATEX_CMD_NAME for selecting the engine.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
@@ -1766,7 +1879,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 +1893,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 +1940,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 +1951,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 +2038,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 +2077,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 +2139,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 +2246,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 +2259,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.
|
||||
@@ -2164,7 +2277,7 @@ HIDE_UNDOC_RELATIONS = YES
|
||||
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
|
||||
# Bell Labs. The other options in this section have no effect if this option is
|
||||
# set to NO
|
||||
# The default value is: YES.
|
||||
# The default value is: NO.
|
||||
|
||||
HAVE_DOT = YES
|
||||
|
||||
@@ -2320,9 +2433,7 @@ DIRECTORY_GRAPH = YES
|
||||
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
|
||||
# to make the SVG files visible in IE 9+ (other browsers do not have this
|
||||
# requirement).
|
||||
# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
|
||||
# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
|
||||
# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
|
||||
# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
|
||||
# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
|
||||
# png:gdiplus:gdiplus.
|
||||
# The default value is: png.
|
||||
|
||||
101
docs/md/config.md
Normal file
101
docs/md/config.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# Crash Course: configuration
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Definitions](#definitions)
|
||||
* [ENTT_NOEXCEPT](#entt_noexcept)
|
||||
* [ENTT_USE_ATOMIC](#entt_use_atomic)
|
||||
* [ENTT_ID_TYPE](#entt_id_type)
|
||||
* [ENTT_PAGE_SIZE](#entt_page_size)
|
||||
* [ENTT_ASSERT](#entt_assert)
|
||||
* [ENTT_DISABLE_ASSERT](#entt_disable_assert)
|
||||
* [ENTT_NO_ETO](#entt_no_eto)
|
||||
* [ENTT_STANDARD_CPP](#entt_standard_cpp)
|
||||
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
`EnTT` doesn't offer many hooks for customization but it certainly offers
|
||||
some.<br/>
|
||||
In the vast majority of cases, users will have no interest in changing the
|
||||
default parameters. For all other cases, the list of possible configurations
|
||||
with which it's possible to adjust the behavior of the library at runtime can be
|
||||
found below.
|
||||
|
||||
# Definitions
|
||||
|
||||
All options are intended as parameters to the compiler (or user-defined macros
|
||||
within the compilation units, if preferred).<br/>
|
||||
Each parameter can result in internal library definitions. It's not recommended
|
||||
to try to also modify these definitions, since there is no guarantee that they
|
||||
will remain stable over time unlike the options below.
|
||||
|
||||
## ENTT_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_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.<br/>
|
||||
Default size of pages (that is, the number of elements they contain) is 4096 but
|
||||
users can adjust it if appropriate. In all case, the chosen value **must** be a
|
||||
power of 2.
|
||||
|
||||
## ENTT_ASSERT
|
||||
|
||||
For performance reasons, `EnTT` doesn't use exceptions or any other control
|
||||
structures. In fact, it offers many features that result in undefined behavior
|
||||
if not used correctly.<br/>
|
||||
To get around this, the library relies on a lot of asserts for the purpose of
|
||||
detecting errors in debug builds. By default, it uses `assert` internally, but
|
||||
users are allowed to overwrite its behavior by setting this variable.
|
||||
|
||||
### ENTT_DISABLE_ASSERT
|
||||
|
||||
Assertions may in turn affect performance to an extent when enabled. Whether
|
||||
`ENTT_ASSERT` is redefined or not, all asserts can be disabled at once by means
|
||||
of this definition.<br/>
|
||||
Note that `ENTT_DISABLE_ASSERT` takes precedence over the redefinition of
|
||||
`ENTT_ASSERT` and is therefore meant to disable all controls no matter what.
|
||||
|
||||
## ENTT_NO_ETO
|
||||
|
||||
In order to reduce memory consumption and increase performance, empty types are
|
||||
never stored by the ECS module of `EnTT`.<br/>
|
||||
Use this variable to treat these types like all others and therefore to create a
|
||||
dedicated storage for them.
|
||||
|
||||
## ENTT_STANDARD_CPP
|
||||
|
||||
`EnTT` mixes non-standard language features with others that are perfectly
|
||||
compliant to offer some of its functionalities.<br/>
|
||||
This definition will prevent the library from using non-standard techniques,
|
||||
that is, functionalities that aren't fully compliant with the standard C++.<br/>
|
||||
While there are no known portability issues at the time of this writing, this
|
||||
should make the library fully portable anyway if needed.
|
||||
711
docs/md/core.md
Normal file
711
docs/md/core.md
Normal file
@@ -0,0 +1,711 @@
|
||||
# Crash Course: core functionalities
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Unique sequential identifiers](#unique-sequential-identifiers)
|
||||
* [Compile-time generator](#compile-time-generator)
|
||||
* [Runtime generator](#runtime-generator)
|
||||
* [Hashed strings](#hashed-strings)
|
||||
* [Wide characters](wide-characters)
|
||||
* [Conflicts](#conflicts)
|
||||
* [Monostate](#monostate)
|
||||
* [Any as in any type](#any-as-in-any-type)
|
||||
* [Small buffer optimization](#small-buffer-optimization)
|
||||
* [Alignment requirement](#alignment-requirement)
|
||||
* [Type support](#type-support)
|
||||
* [Type info](#type-info)
|
||||
* [Almost unique identifiers](#almost-unique-identifiers)
|
||||
* [Type traits](#type-traits)
|
||||
* [Size of](#size-of)
|
||||
* [Is applicable](#is-applicable)
|
||||
* [Constness as](#constness-as)
|
||||
* [Member class type](#member-class-type)
|
||||
* [Integral constant](#integral-constant)
|
||||
* [Tag](#tag)
|
||||
* [Type list and value list](#type-list-and-value-list)
|
||||
* [Utilities](#utilities)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
`EnTT` comes with a bunch of core functionalities mostly used by the other parts
|
||||
of the library itself.<br/>
|
||||
Hardly users will include these features in their code, but it's worth
|
||||
describing what `EnTT` offers so as not to reinvent the wheel in case of need.
|
||||
|
||||
# Unique sequential identifiers
|
||||
|
||||
Sometimes it's useful to be able to give unique, sequential numeric identifiers
|
||||
to types either at compile-time or runtime.<br/>
|
||||
There are plenty of different solutions for this out there and I could have used
|
||||
one of them. However, I decided to spend my time to define a couple of tools
|
||||
that fully embraces what the modern C++ has to offer.
|
||||
|
||||
## Compile-time generator
|
||||
|
||||
To generate sequential numeric identifiers at compile-time, `EnTT` offers the
|
||||
`identifier` class template:
|
||||
|
||||
```cpp
|
||||
// defines the identifiers for the given types
|
||||
using id = entt::identifier<a_type, another_type>;
|
||||
|
||||
// ...
|
||||
|
||||
switch(a_type_identifier) {
|
||||
case id::type<a_type>:
|
||||
// ...
|
||||
break;
|
||||
case id::type<another_type>:
|
||||
// ...
|
||||
break;
|
||||
default:
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
This is all what this class template has to offer: a `type` inline variable that
|
||||
contains a numeric identifier for the given type. It can be used in any context
|
||||
where constant expressions are required.
|
||||
|
||||
As long as the list remains unchanged, identifiers are also guaranteed to be
|
||||
stable across different runs. In case they have been used in a production
|
||||
environment and a type has to be removed, one can just use a placeholder to left
|
||||
the other identifiers unchanged:
|
||||
|
||||
```cpp
|
||||
template<typename> struct ignore_type {};
|
||||
|
||||
using id = entt::identifier<
|
||||
a_type_still_valid,
|
||||
ignore_type<a_type_no_longer_valid>,
|
||||
another_type_still_valid
|
||||
>;
|
||||
```
|
||||
|
||||
Perhaps a bit ugly to see in a codebase but it gets the job done at least.
|
||||
|
||||
## Runtime generator
|
||||
|
||||
To generate sequential numeric identifiers at runtime, `EnTT` offers the
|
||||
`family` class template:
|
||||
|
||||
```cpp
|
||||
// defines a custom generator
|
||||
using id = entt::family<struct my_tag>;
|
||||
|
||||
// ...
|
||||
|
||||
const auto a_type_id = id::type<a_type>;
|
||||
const auto another_type_id = id::type<another_type>;
|
||||
```
|
||||
|
||||
This is all what a _family_ has to offer: a `type` inline variable that contains
|
||||
a numeric identifier for the given type.<br/>
|
||||
The generator is customizable, so as to get different _sequences_ for different
|
||||
purposes if needed.
|
||||
|
||||
Please, note that identifiers aren't guaranteed to be stable across different
|
||||
runs. Indeed it mostly depends on the flow of execution.
|
||||
|
||||
# Hashed strings
|
||||
|
||||
A hashed string is a zero overhead unique identifier. Users can use
|
||||
human-readable identifiers in the codebase while using their numeric
|
||||
counterparts at runtime, thus without affecting performance.<br/>
|
||||
The class has an implicit `constexpr` constructor that chews a bunch of
|
||||
characters. Once created, all what one can do with it is getting back the
|
||||
original string through the `data` member function or converting the instance
|
||||
into a number.<br/>
|
||||
The good part is that a hashed string can be used wherever a constant expression
|
||||
is required and no _string-to-number_ conversion will take place at runtime if
|
||||
used carefully.
|
||||
|
||||
Example of use:
|
||||
|
||||
```cpp
|
||||
auto load(entt::hashed_string::hash_type resource) {
|
||||
// uses the numeric representation of the resource to load and return it
|
||||
}
|
||||
|
||||
auto resource = load(entt::hashed_string{"gui/background"});
|
||||
```
|
||||
|
||||
There is also a _user defined literal_ dedicated to hashed strings to make them
|
||||
more user-friendly:
|
||||
|
||||
```cpp
|
||||
using namespace entt::literals;
|
||||
constexpr auto str = "text"_hs;
|
||||
```
|
||||
|
||||
To use it, remember that all user defined literals in `EnTT` are enclosed in the
|
||||
`entt::literals` namespace. Therefore, the entire namespace or selectively the
|
||||
literal of interest must be explicitly included before each use, a bit like
|
||||
`std::literals`.<br/>
|
||||
Finally, in case users need to create hashed strings at runtime, this class also
|
||||
offers the necessary functionalities:
|
||||
|
||||
```cpp
|
||||
std::string orig{"text"};
|
||||
|
||||
// create a full-featured hashed string...
|
||||
entt::hashed_string str{orig.c_str()};
|
||||
|
||||
// ... or compute only the unique identifier
|
||||
const auto hash = entt::hashed_string::value(orig.c_str());
|
||||
```
|
||||
|
||||
This possibility shouldn't be exploited in tight loops, since the computation
|
||||
takes place at runtime and no longer at compile-time and could therefore impact
|
||||
performance to some degrees.
|
||||
|
||||
## Wide characters
|
||||
|
||||
The hashed string has a design that is close to that of an `std::basic_string`.
|
||||
It means that `hashed_string` is nothing more than an alias for
|
||||
`basic_hashed_string<char>`. For those who want to use the C++ type for wide
|
||||
character representation, there exists also the alias `hashed_wstring` for
|
||||
`basic_hashed_string<wchar_t>`.<br/>
|
||||
In this case, the user defined literal to use to create hashed strings on the
|
||||
fly is `_hws`:
|
||||
|
||||
```cpp
|
||||
constexpr auto str = L"text"_hws;
|
||||
```
|
||||
|
||||
Note that the hash type of the `hashed_wstring` is the same of its counterpart.
|
||||
|
||||
## Conflicts
|
||||
|
||||
The hashed string class uses internally FNV-1a to compute the numeric
|
||||
counterpart of a string. Because of the _pigeonhole principle_, conflicts are
|
||||
possible. This is a fact.<br/>
|
||||
There is no silver bullet to solve the problem of conflicts when dealing with
|
||||
hashing functions. In this case, the best solution seemed to be to give up.
|
||||
That's all.<br/>
|
||||
After all, human-readable unique identifiers aren't something strictly defined
|
||||
and over which users have not the control. Choosing a slightly different
|
||||
identifier is probably the best solution to make the conflict disappear in this
|
||||
case.
|
||||
|
||||
# Monostate
|
||||
|
||||
The monostate pattern is often presented as an alternative to a singleton based
|
||||
configuration system. This is exactly its purpose in `EnTT`. Moreover, this
|
||||
implementation is thread safe by design (hopefully).<br/>
|
||||
Keys are represented by hashed strings, values are basic types like `int`s or
|
||||
`bool`s. Values of different types can be associated to each key, even more than
|
||||
one at a time. Because of this, users must pay attention to use the same type
|
||||
both during an assignment and when they try to read back their data. Otherwise,
|
||||
they will probably incur in unexpected results.
|
||||
|
||||
Example of use:
|
||||
|
||||
```cpp
|
||||
entt::monostate<entt::hashed_string{"mykey"}>{} = true;
|
||||
entt::monostate<"mykey"_hs>{} = 42;
|
||||
|
||||
// ...
|
||||
|
||||
const bool b = entt::monostate<"mykey"_hs>{};
|
||||
const int i = entt::monostate<entt::hashed_string{"mykey"}>{};
|
||||
```
|
||||
|
||||
# Any as in any type
|
||||
|
||||
`EnTT` comes with its own `any` type. It may seem redundant considering that
|
||||
C++17 introduced `std::any`, but it is not (hopefully).<br/>
|
||||
In fact, the _type_ returned by an `std::any` is a const reference to an
|
||||
`std::type_info`, an implementation defined class that's not something everyone
|
||||
wants to see in a software. Furthermore, there is no way to connect it with the
|
||||
type system of the library and therefore with its integrated RTTI support.<br/>
|
||||
Note that this class is largely used internally by the library itself.
|
||||
|
||||
The API is very similar to that of its most famous counterpart, mainly because
|
||||
this class serves the same purpose of being an opaque container for any type of
|
||||
value.<br/>
|
||||
Instances of `any` also minimize the number of allocations by relying on a well
|
||||
known technique called _small buffer optimization_ and a fake vtable.
|
||||
|
||||
Creating an object of the `any` type, whether empty or not, is trivial:
|
||||
|
||||
```cpp
|
||||
// an empty container
|
||||
entt::any empty{};
|
||||
|
||||
// a container for an int
|
||||
entt::any any{0};
|
||||
|
||||
// in place construction
|
||||
entt::any in_place{std::in_place_type<int>, 42};
|
||||
```
|
||||
|
||||
The `any` class takes the burden of destroying the contained element when
|
||||
required, regardless of the storage strategy used for the specific object.<br/>
|
||||
Furthermore, an instance of `any` is not tied to an actual type. Therefore, the
|
||||
wrapper will be reconfigured by assigning it an object of a different type than
|
||||
the one contained, so as to be able to handle the new instance.<br/>
|
||||
When in doubt about the type of object contained, the `type` member function of
|
||||
`any` returns an instance of `type_info` associated with its element, or an
|
||||
invalid `type_info` object if the container is empty. The type is also used
|
||||
internally when comparing two `any` objects:
|
||||
|
||||
```cpp
|
||||
if(any == empty) { /* ... */ }
|
||||
```
|
||||
|
||||
In this case, before proceeding with a comparison, it's verified that the _type_
|
||||
of the two objects is actually the same.<br/>
|
||||
Refer to the `EnTT` type system documentation for more details on how
|
||||
`type_info` works and on possible risks of a comparison.
|
||||
|
||||
A particularly interesting feature of this class is that it can also be used as
|
||||
an opaque container for const and non-const references:
|
||||
|
||||
```cpp
|
||||
int value = 42;
|
||||
|
||||
// reference construction
|
||||
entt::any any{std::ref(value)};
|
||||
entt::any cany{std::cref(value)};
|
||||
|
||||
// alias construction
|
||||
int value = 42;
|
||||
entt::any in_place{std::in_place_type<int &>, &value};
|
||||
```
|
||||
|
||||
In other words, whenever `any` intercepts a `reference_wrapper` or is explicitly
|
||||
told that users want to construct an alias, it acts as a pointer to the original
|
||||
instance rather than making a copy of it or moving it internally. The contained
|
||||
object is never destroyed and users must ensure that its lifetime exceeds that
|
||||
of the container.<br/>
|
||||
Similarly, it's possible to create non-owning copies of `any` from an existing
|
||||
object:
|
||||
|
||||
```cpp
|
||||
// aliasing constructor
|
||||
entt::any ref = other.as_ref();
|
||||
```
|
||||
|
||||
In this case, it doesn't matter if the original container actually holds an
|
||||
object or acts already as a reference for unmanaged elements, the new instance
|
||||
thus created won't create copies and will only serve as a reference for the
|
||||
original item.<br/>
|
||||
This means that, starting from the example above, both `ref` and` other` will
|
||||
point to the same object, whether it's initially contained in `other` or already
|
||||
an unmanaged element.
|
||||
|
||||
As a side note, it's worth mentioning that, while everything works transparently
|
||||
when it comes to non-const references, there are some exceptions when it comes
|
||||
to const references.<br/>
|
||||
In particular, the `data` member function invoked on a non-const instance of
|
||||
`any` that wraps a const reference will return a null pointer in all cases.
|
||||
|
||||
To cast an instance of `any` to a type, the library offers a set of `any_cast`
|
||||
functions in all respects similar to their most famous counterparts.<br/>
|
||||
The only difference is that, in the case of `EnTT`, these won't raise exceptions
|
||||
but will only trigger an assert in debug mode, otherwise resulting in undefined
|
||||
behavior in case of misuse in release mode.
|
||||
|
||||
## Small buffer optimization
|
||||
|
||||
The `any` class uses a technique called _small buffer optimization_ to reduce
|
||||
the number of allocations where possible.<br/>
|
||||
The default reserved size for an instance of `any` is `sizeof(double[2])`.
|
||||
However, this is also configurable if needed. In fact, `any` is defined as an
|
||||
alias for `basic_any<Len>`, where `Len` is the size above.<br/>
|
||||
Users can easily set a custom size or define their own aliases:
|
||||
|
||||
```cpp
|
||||
using my_any = entt::basic_any<sizeof(double[4])>;
|
||||
```
|
||||
|
||||
This feature, in addition to allowing the choice of a size that best suits the
|
||||
needs of an application, also offers the possibility of forcing dynamic creation
|
||||
of objects during construction.<br/>
|
||||
In other terms, if the size is 0, `any` avoids the use of any optimization and
|
||||
always dynamically allocates objects (except for aliasing cases).
|
||||
|
||||
Note that the size of the internal storage as well as the alignment requirements
|
||||
are directly part of the type and therefore contribute to define different types
|
||||
that won't be able to interoperate with each other.
|
||||
|
||||
## Alignment requirement
|
||||
|
||||
The alignment requirement is optional and by default the most stringent (the
|
||||
largest) for any object whose size is at most equal to the one provided.<br/>
|
||||
The `basic_any` class template inspects the alignment requirements in each case,
|
||||
even when not provided and may decide not to use the small buffer optimization
|
||||
in order to meet them.
|
||||
|
||||
The alignment requirement is provided as an optional second parameter following
|
||||
the desired size for the internal storage:
|
||||
|
||||
```cpp
|
||||
using my_any = entt::basic_any<sizeof(double[4]), alignof(double[4])>;
|
||||
```
|
||||
|
||||
Note that the alignment requirements as well as the size of the internal storage
|
||||
are directly part of the type and therefore contribute to define different types
|
||||
that won't be able to interoperate with each other.
|
||||
|
||||
# Type support
|
||||
|
||||
`EnTT` provides some basic information about types of all kinds.<br/>
|
||||
It also offers additional features that are not yet available in the standard
|
||||
library or that will never be.
|
||||
|
||||
## Type info
|
||||
|
||||
The `type_info` class isn't a drop-in replacement for `std::type_info` but can
|
||||
provide similar information which are not implementation defined and don't
|
||||
require to enable RTTI.<br/>
|
||||
Therefore, they can sometimes be even more reliable than those obtained
|
||||
otherwise.
|
||||
|
||||
A type info object is an opaque class that is also copy and move constructible.
|
||||
This class is returned by the `type_id` function template:
|
||||
|
||||
```cpp
|
||||
auto info = entt::type_id<a_type>();
|
||||
```
|
||||
|
||||
These are the information made available by this object:
|
||||
|
||||
* The unique, sequential identifier associated with a given type:
|
||||
|
||||
```cpp
|
||||
auto index = entt::type_id<a_type>().seq();
|
||||
```
|
||||
|
||||
This is also an alias for the following:
|
||||
|
||||
```cpp
|
||||
auto index = entt::type_seq<a_type>::value();
|
||||
```
|
||||
|
||||
The returned value isn't guaranteed to be stable across different runs.
|
||||
However, it can be very useful as index in associative and unordered
|
||||
associative containers or for positional accesses in a vector or an array.
|
||||
|
||||
So as not to conflict with the other tools available, the `family` class isn't
|
||||
used to generate these indexes. Therefore, the numeric identifiers returned by
|
||||
the two tools may differ.<br/>
|
||||
On the other hand, this leaves users with full powers over the `family` class
|
||||
and therefore the generation of custom runtime sequences of indices for their
|
||||
own purposes, if necessary.
|
||||
|
||||
An external generator can also be used if needed. In fact, `type_seq` 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_seq<Type, std::void_d<decltype(Type::index())>> {
|
||||
static entt::id_type value() ENTT_NOEXCEPT {
|
||||
return Type::index();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Note that indexes **must** still be generated sequentially in this case.<br/>
|
||||
The tool is widely used within `EnTT`. Generating indices not sequentially
|
||||
would break an assumption and would likely lead to undesired behaviors.
|
||||
|
||||
* The hash value associated with a given type:
|
||||
|
||||
```cpp
|
||||
auto hash = entt::type_id<a_type>().hash();
|
||||
```
|
||||
|
||||
This is also an alias for the following:
|
||||
|
||||
```cpp
|
||||
auto hash = entt::type_hash<a_type>::value();
|
||||
```
|
||||
|
||||
In general, the `value` function exposed by `type_hash` is also `constexpr`
|
||||
but this isn't guaranteed for all compilers and platforms (although it's valid
|
||||
with the most well-known and popular ones).<br/>
|
||||
The `hash` function offered by the type info object isn't `constexpr` in any
|
||||
case instead.
|
||||
|
||||
This function **can** use non-standard features of the language for its own
|
||||
purposes. This makes it possible to provide compile-time identifiers that
|
||||
remain stable across different runs.<br/>
|
||||
In all cases, users can prevent the library from using these features by means
|
||||
of the `ENTT_STANDARD_CPP` definition. In this case, there is no guarantee
|
||||
that identifiers remain stable across executions. Moreover, they are generated
|
||||
at runtime and are no longer a compile-time thing.
|
||||
|
||||
As for `type_seq`, also `type_hash` is a _sfinae-friendly_ class that can be
|
||||
specialized in order to customize its behavior globally or on a per-type or
|
||||
per-traits basis.
|
||||
|
||||
* The name associated with a given type:
|
||||
|
||||
```cpp
|
||||
auto name = entt::type_id<my_type>().name();
|
||||
```
|
||||
|
||||
This is also an alias for the following:
|
||||
|
||||
```cpp
|
||||
auto name = entt::type_name<a_type>::value();
|
||||
```
|
||||
|
||||
The name associated with a type is extracted from some information generally
|
||||
made available by the compiler in use. Therefore, it may differ depending on
|
||||
the compiler and may be empty in the event that this information isn't
|
||||
available.<br/>
|
||||
For example, given the following class:
|
||||
|
||||
```cpp
|
||||
struct my_type { /* ... */ };
|
||||
```
|
||||
|
||||
The name is `my_type` when compiled with GCC or CLang and `struct my_type`
|
||||
when MSVC is in use.<br/>
|
||||
Most of the time the name is also retrieved at compile-time and is therefore
|
||||
always returned through an `std::string_view`. Users can easily access it and
|
||||
modify it as needed, for example by removing the word `struct` to standardize
|
||||
the result. `EnTT` won't do this for obvious reasons, since it requires
|
||||
copying and creating a new string potentially at runtime.
|
||||
|
||||
This function **can** use non-standard features of the language for its own
|
||||
purposes. Users can prevent the library from using non-standard features by
|
||||
means of the `ENTT_STANDARD_CPP` definition. In this case, the name will be
|
||||
empty by default.
|
||||
|
||||
As for `type_seq`, also `type_name` is a _sfinae-friendly_ class that can be
|
||||
specialized in order to customize its behavior globally or on a per-type or
|
||||
per-traits basis.
|
||||
|
||||
### Almost unique identifiers
|
||||
|
||||
Since the default non-standard, compile-time implementation of `type_hash` makes
|
||||
use of hashed strings, it may happen that two types are assigned the same hash
|
||||
value.<br/>
|
||||
In fact, although this is quite rare, it's not entirely excluded.
|
||||
|
||||
Another case where two types are assigned the same identifier is when classes
|
||||
from different contexts (for example two or more libraries loaded at runtime)
|
||||
have the same fully qualified name. In this case, also `type_name` will return
|
||||
the same value for the two types.<br/>
|
||||
Fortunately, there are several easy ways to deal with this:
|
||||
|
||||
* The most trivial one is to define the `ENTT_STANDARD_CPP` macro. Runtime
|
||||
identifiers don't suffer from the same problem in fact. However, this solution
|
||||
doesn't work well with a plugin system, where the libraries aren't linked.
|
||||
|
||||
* Another possibility is to specialize the `type_name` class for one of the
|
||||
conflicting types, in order to assign it a custom identifier. This is probably
|
||||
the easiest solution that also preserves the feature of the tool.
|
||||
|
||||
* A fully customized identifier generation policy (based for example on enum
|
||||
classes or preprocessing steps) may represent yet another option.
|
||||
|
||||
These are just some examples of possible approaches to the problem but there are
|
||||
many others. As already mentioned above, since users have full control over
|
||||
their types, this problem is in any case easy to solve and should not worry too
|
||||
much.<br/>
|
||||
In all likelihood, it will never happen to run into a conflict anyway.
|
||||
|
||||
## Type traits
|
||||
|
||||
A handful of utilities and traits not present in the standard template library
|
||||
but which can be useful in everyday life.<br/>
|
||||
This list **is not** exhaustive and contains only some of the most useful
|
||||
classes. Refer to the inline documentation for more information on the features
|
||||
offered by this module.
|
||||
|
||||
### Size of
|
||||
|
||||
The standard operator `sizeof` complains when users provide it for example with
|
||||
function or incomplete types. On the other hand, it's guaranteed that its result
|
||||
is always nonzero, even if applied to an empty class type.<br/>
|
||||
This small class combines the two and offers an alternative to `sizeof` that
|
||||
works under all circumstances, returning zero if the type isn't supported:
|
||||
|
||||
```cpp
|
||||
const auto size = entt::size_of_v<void>;
|
||||
```
|
||||
|
||||
### Is applicable
|
||||
|
||||
The standard library offers the great `std::is_invocable` trait in several
|
||||
forms. This takes a function type and a series of arguments and returns true if
|
||||
the condition is satisfied.<br/>
|
||||
Moreover, users are also provided with `std::apply`, a tool for combining
|
||||
invocable elements and tuples of arguments.
|
||||
|
||||
It would therefore be a good idea to have a variant of `std::is_invocable` that
|
||||
also accepts its arguments in the form of a tuple-like type, so as to complete
|
||||
the offer:
|
||||
|
||||
```cpp
|
||||
constexpr bool result = entt::is_applicable<Func, std::tuple<a_type, another_type>>;
|
||||
```
|
||||
|
||||
This trait is built on top of `std::is_invocable` and does nothing but unpack a
|
||||
tuple-like type and simplify the code at the call site.
|
||||
|
||||
### Constness as
|
||||
|
||||
An utility to easily transfer the constness of a type to another type:
|
||||
|
||||
```cpp
|
||||
// type is const dst_type because of the constness of src_type
|
||||
using type = entt::constness_as_t<dst_type, const src_type>;
|
||||
```
|
||||
|
||||
The trait is subject to the rules of the language. Therefore, for example,
|
||||
transferring constness between references won't give the desired effect.
|
||||
|
||||
### Member class type
|
||||
|
||||
The `auto` template parameter introduced with C++17 made it possible to simplify
|
||||
many class templates and template functions but also made the class type opaque
|
||||
when members are passed as template arguments.<br/>
|
||||
The purpose of this utility is to extract the class type in a few lines of code:
|
||||
|
||||
```cpp
|
||||
template<typename Member>
|
||||
using clazz = entt::member_class_t<Member>;
|
||||
```
|
||||
|
||||
### Integral constant
|
||||
|
||||
Since `std::integral_constant` may be annoying because of its form that requires
|
||||
to specify both a type and a value of that type, there is a more user-friendly
|
||||
shortcut for the creation of integral constants.<br/>
|
||||
This shortcut is the alias template `entt::integral_constant`:
|
||||
|
||||
```cpp
|
||||
constexpr auto constant = entt::integral_constant<42>;
|
||||
```
|
||||
|
||||
Among the other uses, when combined with a hashed string it helps to define tags
|
||||
as human-readable _names_ where actual types would be required otherwise:
|
||||
|
||||
```cpp
|
||||
constexpr auto enemy_tag = entt::integral_constant<"enemy"_hs>;
|
||||
registry.emplace<enemy_tag>(entity);
|
||||
```
|
||||
|
||||
### Tag
|
||||
|
||||
Since `id_type` is very important and widely used in `EnTT`, there is a more
|
||||
user-friendly shortcut for the creation of integral constants based on it.<br/>
|
||||
This shortcut is the alias template `entt::tag`.
|
||||
|
||||
If used in combination with hashed strings, it helps to use human-readable names
|
||||
where types would be required otherwise. As an example:
|
||||
|
||||
```cpp
|
||||
registry.emplace<entt::tag<"enemy"_hs>>(entity);
|
||||
```
|
||||
|
||||
However, this isn't the only permitted use. Literally any value convertible to
|
||||
`id_type` is a good candidate, such as the named constants of an unscoped enum.
|
||||
|
||||
### Type list and value list
|
||||
|
||||
There is no respectable library where the much desired _type list_ can be
|
||||
missing.<br/>
|
||||
`EnTT` is no exception and provides (making extensive use of it internally) the
|
||||
`type_list` type, in addition to its `value_list` counterpart dedicated to
|
||||
non-type template parameters.
|
||||
|
||||
Here is a (possibly incomplete) list of the functionalities that come with a
|
||||
type list:
|
||||
|
||||
* `type_list_element[_t]` to get the N-th element of a type list.
|
||||
* `type_list_cast[_t]` and a handy `operator+` to concatenate type lists.
|
||||
* `type_list_unique[_t]` to remove duplicate types from a type list.
|
||||
* `type_list_contains[_v]` to know if a type list contains a given type.
|
||||
* `type_list_diff[_t]` to remove types from type lists.
|
||||
|
||||
I'm also pretty sure that more and more utilities will be added over time as
|
||||
needs become apparent.<br/>
|
||||
Many of these functionalities also exist in their version dedicated to value
|
||||
lists. We therefore have `value_list_element[_v]` as well as
|
||||
`value_list_cat[_t]`and so on.
|
||||
|
||||
# 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.
|
||||
1981
docs/md/entity.md
Normal file
1981
docs/md/entity.md
Normal file
File diff suppressed because it is too large
Load Diff
218
docs/md/faq.md
Normal file
218
docs/md/faq.md
Normal file
@@ -0,0 +1,218 @@
|
||||
# Frequently Asked Questions
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [FAQ](#faq)
|
||||
* [Why is my debug build on Windows so slow?](#why-is-my-debug-build-on-windows-so-slow)
|
||||
* [How can I represent hierarchies with my components?](#how-can-i-represent-hierarchies-with-my-components)
|
||||
* [Custom entity identifiers: yay or nay?](#custom-entity-identifiers-yay-or-nay)
|
||||
* [Warning C4307: integral constant overflow](#warning-C4307-integral-constant-overflow)
|
||||
* [Warning C4003: the min, the max and the macro](#warning-C4003-the-min-the-max-and-the-macro)
|
||||
* [The standard and the non-copyable types](#the-standard-and-the-non-copyable-types)
|
||||
* [Which functions trigger which signals](#which-functions-trigger-which-signals)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
This is a constantly updated section where I'll try to put the answers to the
|
||||
most frequently asked questions.<br/>
|
||||
If you don't find your answer here, there are two cases: nobody has done it yet
|
||||
or this section needs updating. In both cases, try to
|
||||
[open a new issue](https://github.com/skypjack/entt/issues/new) or enter the
|
||||
[gitter channel](https://gitter.im/skypjack/entt) and ask your question.
|
||||
Probably someone already has an answer for you and we can then integrate this
|
||||
part of the documentation.
|
||||
|
||||
# FAQ
|
||||
|
||||
## Why is my debug build on Windows so slow?
|
||||
|
||||
`EnTT` is an experimental project that I also use to keep me up-to-date with the
|
||||
latest revision of the language and the standard library. For this reason, it's
|
||||
likely that some classes you're working with are using standard containers under
|
||||
the hood.<br/>
|
||||
Unfortunately, it's known that the standard containers aren't particularly
|
||||
performing in debugging (the reasons for this go beyond this document) and are
|
||||
even less so on Windows apparently. Fortunately this can also be mitigated a
|
||||
lot, achieving good results in many cases.
|
||||
|
||||
First of all, there are two things to do in a Windows project:
|
||||
|
||||
* Disable the [`/JMC`](https://docs.microsoft.com/cpp/build/reference/jmc)
|
||||
option (_Just My Code_ debugging), available starting in Visual Studio 2017
|
||||
version 15.8.
|
||||
|
||||
* Set the [`_ITERATOR_DEBUG_LEVEL`](https://docs.microsoft.com/cpp/standard-library/iterator-debug-level)
|
||||
macro to 0. This will disable checked iterators and iterator debugging.
|
||||
|
||||
Moreover, the macro `ENTT_ASSERT` should be redefined to disable internal checks
|
||||
made by `EnTT` in debug:
|
||||
|
||||
```cpp
|
||||
#define ENTT_ASSERT(...) ((void)0)
|
||||
```
|
||||
|
||||
These asserts are introduced to help the users but they require to access to the
|
||||
underlying containers and therefore risk ruining the performance in some cases.
|
||||
|
||||
With these changes, debug performance should increase enough for most cases. If
|
||||
you want something more, you can can also switch to an optimization level `O0`
|
||||
or preferably `O1`.
|
||||
|
||||
## How can I represent hierarchies with my components?
|
||||
|
||||
This is one of the first questions that anyone makes when starting to work with
|
||||
the entity-component-system architectural pattern.<br/>
|
||||
There are several approaches to the problem and what’s the best one depends
|
||||
mainly on the real problem one is facing. In all cases, how to do it doesn't
|
||||
strictly depend on the library in use, but the latter can certainly allow or
|
||||
not different techniques depending on how the data are laid out.
|
||||
|
||||
I tried to describe some of the techniques that fit well with the model of
|
||||
`EnTT`. [Here](https://skypjack.github.io/2019-06-25-ecs-baf-part-4/) is the
|
||||
first post of a series that tries to explore the problem. More will probably
|
||||
come in future.
|
||||
|
||||
Long story short, you can always define a tree where the nodes expose implicit
|
||||
lists of children by means of the following type:
|
||||
|
||||
```cpp
|
||||
struct relationship {
|
||||
std::size_t children{};
|
||||
entt::entity first{entt::null};
|
||||
entt::entity prev{entt::null};
|
||||
entt::entity next{entt::null};
|
||||
entt::entity parent{entt::null};
|
||||
// ... other data members ...
|
||||
};
|
||||
```
|
||||
|
||||
The sort functionalities of `EnTT`, the groups and all the other features of the
|
||||
library can help then to get the best in terms of data locality and therefore
|
||||
performance from this component.
|
||||
|
||||
## Custom entity identifiers: yay or nay?
|
||||
|
||||
Custom entity identifiers are definitely a good idea in two cases at least:
|
||||
|
||||
* 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.
|
||||
|
||||
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
|
||||
enum class entity: std::uint32_t {};
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
According to [this](https://github.com/skypjack/entt/issues/121) issue, using a
|
||||
hashed string under VS could generate a warning.<br/>
|
||||
First of all, I want to reassure you: it's expected and harmless. However, it
|
||||
can be annoying.
|
||||
|
||||
To suppress it and if you don't want to suppress all the other warnings as well,
|
||||
here is a workaround in the form of a macro:
|
||||
|
||||
```cpp
|
||||
#if defined(_MSC_VER)
|
||||
#define HS(str) __pragma(warning(suppress:4307)) entt::hashed_string{str}
|
||||
#else
|
||||
#define HS(str) entt::hashed_string{str}
|
||||
#endif
|
||||
```
|
||||
|
||||
With an example of use included:
|
||||
|
||||
```cpp
|
||||
constexpr auto identifier = HS("my/resource/identifier");
|
||||
```
|
||||
|
||||
Thanks to [huwpascoe](https://github.com/huwpascoe) for the courtesy.
|
||||
|
||||
## Warning C4003: the min, the max and the macro
|
||||
|
||||
On Windows, a header file defines two macros `min` and `max` which may result in
|
||||
conflicts with their counterparts in the standard library and therefore in
|
||||
errors during compilation.
|
||||
|
||||
It's a pretty big problem but fortunately it's not a problem of `EnTT` and there
|
||||
is a fairly simple solution to it.<br/>
|
||||
It consists in defining the `NOMINMAX` macro before to include any other header
|
||||
so as to get rid of the extra definitions:
|
||||
|
||||
```cpp
|
||||
#define NOMINMAX
|
||||
```
|
||||
|
||||
Please refer to [this](https://github.com/skypjack/entt/issues/96) issue for
|
||||
more details.
|
||||
|
||||
## The standard and the non-copyable types
|
||||
|
||||
`EnTT` uses internally the trait `std::is_copy_constructible_v` to check if a
|
||||
component is actually copyable. This trait doesn't check if an object can
|
||||
actually be copied but only verifies if there is a copy constructor
|
||||
available.<br/>
|
||||
This can lead to surprising results due to some idiosyncrasies of the standard
|
||||
mainly related to the need to guarantee backward compatibility.
|
||||
|
||||
For example, `std::vector` defines a copy constructor no matter if its value
|
||||
type is copyable or not. As a result, `std::is_copy_constructible_v` is true
|
||||
for the following specialization:
|
||||
|
||||
```cpp
|
||||
struct type {
|
||||
std::vector<std::unique_ptr<action>> vec;
|
||||
};
|
||||
```
|
||||
|
||||
When trying to assign an instance of this type to an entity in the ECS part,
|
||||
this may trigger a compilation error because we cannot really make a copy of
|
||||
it.<br/>
|
||||
As a workaround, users can mark the type explicitly as non-copyable:
|
||||
|
||||
```cpp
|
||||
struct type {
|
||||
type(const type &) = delete;
|
||||
type & operator=(const type &) = delete;
|
||||
|
||||
std::vector<std::unique_ptr<action>> vec;
|
||||
};
|
||||
```
|
||||
|
||||
Unfortunately, this will also disable aggregate initialization.
|
||||
|
||||
## Which functions trigger which signals
|
||||
|
||||
The `registry` class offers three signals that are emitted following specific
|
||||
operations. Maybe not everyone knows what these operations are, though.<br/>
|
||||
If this isn't clear, below you can find a _vademecum_ for this purpose:
|
||||
|
||||
* `on_created` is invoked when a component is first added (neither modified nor
|
||||
replaced) to an entity.
|
||||
* `on_update` is called whenever an existing component is modified or replaced.
|
||||
* `on_destroyed` is called when a component is explicitly or implicitly removed
|
||||
from an entity.
|
||||
|
||||
Among the most controversial functions can be found `emplace_or_replace` and
|
||||
`destroy`. However, following the above rules, it's quite simple to know what
|
||||
will happen.<br/>
|
||||
In the first case, `on_created` is invoked if the entity has not the component,
|
||||
otherwise the latter is replaced and therefore `on_update` is triggered. As for
|
||||
the second case, components are removed from their entities and thus freed when
|
||||
they are recycled. It means that `on_destroyed` is triggered for every component
|
||||
owned by the entity that is destroyed.
|
||||
127
docs/md/lib.md
Normal file
127
docs/md/lib.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Push EnTT across boundaries
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Working across boundaries](#working-across-boundaries)
|
||||
* [The EnTT way](#the-entt-way)
|
||||
* [Meta context](#meta-context)
|
||||
* [Memory management](#memory-management)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Working across boundaries
|
||||
|
||||
`EnTT` has historically had a limit when used across boundaries on Windows in
|
||||
general and on GNU/Linux when default visibility was set to hidden. The
|
||||
limitation was mainly due to a custom utility used to assign unique, sequential
|
||||
identifiers with different types.<br/>
|
||||
Fortunately, nowadays using `EnTT` across boundaries is easier. However, use in
|
||||
standalone applications is favored and user intervention is otherwise required.
|
||||
|
||||
## The EnTT way
|
||||
|
||||
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_hash` class template is how identifiers are generated and thus made
|
||||
available to the rest of the library. The `type_seq` class template makes all
|
||||
types _indexable_ instead, so as to speed up the lookup.
|
||||
|
||||
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_seq` confuses the other classes by giving potentially wrong
|
||||
information to them.<br/>
|
||||
To avoid problems, it's required to provide a custom generator. Briefly, it's
|
||||
necessary to specialize the `type_seq` class and make it point to a context
|
||||
that is also shared between the main application and the dynamically loaded
|
||||
libraries or plugins.<br/>
|
||||
This will make the type system available to the whole application, not just to
|
||||
a particular tool such as the registry or the dispatcher. It means that a call
|
||||
to `type_seq::value()` will return the same identifier for the same type from
|
||||
both sides of a boundary and can be used reliably for any purpose.
|
||||
|
||||
For anyone who needs more details, the test suite contains multiple examples
|
||||
covering the most common cases (see the `lib` directory for all details).<br/>
|
||||
It goes without saying that it's impossible to cover all 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 already to a static context to which the visible components
|
||||
are attached and different contexts don't relate to each other, they must be
|
||||
_shared_ to allow the use of meta types across boundaries.
|
||||
|
||||
Sharing a context is trivial though. First of all, the local one must be
|
||||
acquired in the main space:
|
||||
|
||||
```cpp
|
||||
entt::meta_ctx ctx{};
|
||||
```
|
||||
|
||||
Then, it must passed to the receiving space that will set it as its global
|
||||
context, thus releasing the local one that remains available but is no longer
|
||||
referred to by the runtime reflection system:
|
||||
|
||||
```cpp
|
||||
entt::meta_ctx::bind(ctx);
|
||||
```
|
||||
|
||||
From now on, both spaces will refer to the same context and on it will be
|
||||
attached the new visible meta types, no matter where they are created.<br/>
|
||||
A context can also be reset and then associated again locally as:
|
||||
|
||||
```cpp
|
||||
entt::meta_ctx::bind(entt::meta_ctx{});
|
||||
```
|
||||
|
||||
This is allowed because local and global contexts are separated. Therefore, it's
|
||||
always possible to make the local context the current one again.
|
||||
|
||||
Before to release a context, all locally registered types should be reset to
|
||||
avoid dangling references. Otherwise, if a type is accessed from another space
|
||||
by name, there could be an attempt to address its parts that are no longer
|
||||
available.
|
||||
|
||||
## Memory Management
|
||||
|
||||
There is another subtle problem due to memory management that can lead to
|
||||
headaches.<br/>
|
||||
It can occur where there are pools of objects (such as components or events)
|
||||
dynamically created on demand. This is usually not a problem when working with
|
||||
linked libraries that rely on the same dynamic runtime. However, it can occur in
|
||||
the case of plugins or statically linked runtimes.
|
||||
|
||||
As an example, imagine creating an instance of `registry` in the main executable
|
||||
and sharing it with a plugin. If the latter starts working with a component that
|
||||
is unknown to the former, a dedicated pool is created within the registry on
|
||||
first use.<br/>
|
||||
As one can guess, this pool is instantiated on a different side of the boundary
|
||||
from the `registry`. Therefore, the instance is now managing memory from
|
||||
different spaces and this can quickly lead to crashes if not properly addressed.
|
||||
|
||||
To overcome the risk, it's recommended to use well-defined interfaces that make
|
||||
fundamental types pass through the boundaries, isolating the instances of the
|
||||
`EnTT` classes from time to time and as appropriate.<br/>
|
||||
Refer to the test suite for some examples, read the documentation available
|
||||
online about this type of issues or consult someone who has already had such
|
||||
experiences to avoid problems.
|
||||
205
docs/md/links.md
Normal file
205
docs/md/links.md
Normal file
@@ -0,0 +1,205 @@
|
||||
# EnTT in Action
|
||||
|
||||
`EnTT` is widely used in private and commercial applications. I cannot even
|
||||
mention most of them because of some signatures I put on some documents time
|
||||
ago. Fortunately, there are also people who took the time to implement open
|
||||
source projects based on `EnTT` and didn't hold back when it came to documenting
|
||||
them.
|
||||
|
||||
Below an incomplete list of games, applications and articles that can be used as
|
||||
a reference. Where I put the word _apparently_ means that the use of `EnTT` is
|
||||
documented but the authors didn't make explicit announcements or contacted me
|
||||
directly.
|
||||
|
||||
I hope this list can grow much more in the future:
|
||||
|
||||
* Games:
|
||||
* [Minecraft](https://minecraft.net/en-us/attribution/) by
|
||||
[Mojang](https://mojang.com/): of course, **that** Minecraft, see the
|
||||
open source attributions page for more details.
|
||||
* [Minecraft Earth](https://www.minecraft.net/en-us/about-earth) by
|
||||
[Mojang](https://mojang.com/): an augmented reality game for mobile, that
|
||||
lets users bring Minecraft into the real world.
|
||||
* [TiltedOnline](https://github.com/tiltedphoques/TiltedOnline) by
|
||||
[Tilted Phoques](https://github.com/tiltedphoques): Skyrim and Fallout 4 mod
|
||||
to play online.
|
||||
* [Antkeeper](https://github.com/antkeeper/antkeeper-source): an ant colony
|
||||
simulation [game](https://antkeeper.com/).
|
||||
* [War of Rights](https://store.steampowered.com/app/424030/War_of_Rights/): a
|
||||
multiplayer game set during the perilous days of the American Civil War, by
|
||||
Campfire Games.
|
||||
* [Openblack](https://github.com/openblack/openblack): open source
|
||||
reimplementation of the game _Black & White_ (2001).
|
||||
* [Land of the Rair](https://github.com/LandOfTheRair/core2): the new backend
|
||||
of [a retro-style MUD](https://rair.land/) for the new age.
|
||||
* [Face Smash](https://play.google.com/store/apps/details?id=com.gamee.facesmash):
|
||||
a game to play with your face.
|
||||
* [EnTT Pacman](https://github.com/Kerndog73/EnTT-Pacman): an example of how
|
||||
to make Pacman with `EnTT`.
|
||||
* [Wacman](https://github.com/carlfindahl/wacman): a pacman clone with OpenGL.
|
||||
* [Classic Tower Defence](https://github.com/kerndog73/Classic-Tower-Defence):
|
||||
a tiny little tower defence game featuring a homemade font.
|
||||
[Check it out](https://indi-kernick.itch.io/classic-tower-defence).
|
||||
* [The Machine](https://github.com/Kerndog73/The-Machine): a box pushing
|
||||
puzzler with logic gates and other cool stuff.
|
||||
[Check it out](https://indi-kernick.itch.io/the-machine-web-version).
|
||||
* [EnTTPong](https://github.com/DomRe/EnttPong): a basic game made to showcase
|
||||
different parts of EnTT and C++17.
|
||||
* [Randballs](https://github.com/gale93/randballs): simple `SFML` and `EnTT`
|
||||
playground.
|
||||
* [EnTT Tower Defense](https://github.com/Daivuk/tddod): a data oriented tower
|
||||
defense example.
|
||||
* [EnTT Breakout](https://github.com/vblanco20-1/entt-breakout): simple
|
||||
example of a breakout game, using `SDL` and `EnTT`.
|
||||
* [Arcade puzzle game with EnTT](https://github.com/MasonRG/ArcadePuzzleGame):
|
||||
arcade puzzle game made in C++ using the `SDL2` and `EnTT` libraries.
|
||||
* [Snake with EnTT](https://github.com/MasonRG/SnakeGame): simple snake game
|
||||
made in C++ with the `SDL2` and `EnTT` libraries.
|
||||
* [Mirrors lasers and robots](https://github.com/guillaume-haerinck/imac-tower-defense):
|
||||
a small tower defense game based on mirror orientation.
|
||||
* [PopHead](https://github.com/SPC-Some-Polish-Coders/PopHead/): 2D, Zombie,
|
||||
RPG game made from scratch in C++.
|
||||
* [Robotligan](https://github.com/Trisslotten/robotligan): multiplayer
|
||||
football game.
|
||||
* [DungeonSlayer](https://github.com/alohaeee/DungeonSlayer): 2D game made
|
||||
from scratch in C++.
|
||||
* [3DGame](https://github.com/kwarkGorny/3DGame): 2.5D top-down space shooter.
|
||||
* [Pulcher](https://github.com/AODQ/pulcher): 2D cross-platform game inspired
|
||||
by Quake.
|
||||
* [Destroid](https://github.com/tyrannicaltoucan/destroid): _one-bazillionth_
|
||||
arcade game about shooting dirty rocks in space, inspired by Asteroids.
|
||||
* [Wanderer](https://github.com/albin-johansson/wanderer): a 2D exploration
|
||||
based indie game.
|
||||
* [Spelunky® Classic remake](https://github.com/dbeef/spelunky-psp): A truly
|
||||
multiplatform experience with a rewrite from scratch.
|
||||
|
||||
* Engines and the like:
|
||||
* [Aether Engine](https://hadean.com/spatial-simulation/)
|
||||
[v1.1+](https://docs.hadean.com/v1.1/Licenses/) by
|
||||
[Hadean](https://hadean.com/): a library designed for spatially partitioning
|
||||
agent-based simulations.
|
||||
* [Fling Engine](https://github.com/flingengine/FlingEngine): a Vulkan game
|
||||
engine with a focus on data oriented design.
|
||||
* [NovusCore](https://github.com/novuscore/NovusCore): a modern take on World
|
||||
of Warcraft emulation.
|
||||
* [Chrysalis](https://github.com/ivanhawkes/Chrysalis): action RPG SDK for
|
||||
CRYENGINE games.
|
||||
* [LM-Engine](https://github.com/Lawrencemm/LM-Engine): the Vim of game
|
||||
engines.
|
||||
* [Edyn](https://github.com/xissburg/edyn): a real-time physics engine
|
||||
organized as an ECS.
|
||||
* [MushMachine](https://github.com/MadeOfJelly/MushMachine): engine...
|
||||
vrooooommm.
|
||||
* [Antara Gaming SDK](https://github.com/KomodoPlatform/antara-gaming-sdk):
|
||||
the Komodo Gaming Software Development Kit.
|
||||
* [Apparently](https://teamwisp.github.io/credits/)
|
||||
[Wisp](https://teamwisp.github.io/product/) by
|
||||
[Team Wisp](https://teamwisp.github.io/): an advanced real-time ray tracing
|
||||
renderer built for the demands of video game artists.
|
||||
* [shiva](https://github.com/Milerius/shiva): modern C++ engine with
|
||||
modularity.
|
||||
* [ImGui/EnTT editor](https://github.com/Green-Sky/imgui_entt_entity_editor):
|
||||
a drop-in, single-file entity editor for `EnTT` that uses `ImGui` as
|
||||
graphical backend (with
|
||||
[demo code](https://github.com/Green-Sky/imgui_entt_entity_editor_demo)).
|
||||
* [SgOgl](https://github.com/stwe/SgOgl): a game engine library for OpenGL
|
||||
developed for educational purposes.
|
||||
* [Lumos](https://github.com/jmorton06/Lumos): game engine written in C++
|
||||
using OpenGL and Vulkan.
|
||||
* [Silvanus](https://github.com/hobbyistmaker/silvanus): Silvanus Fusion 360
|
||||
Box Generator.
|
||||
* [Lina Engine](https://github.com/inanevin/LinaEngine): an open-source,
|
||||
modular, tiny and fast C++ game engine, aimed to develop 3D desktop games.
|
||||
* [Spike](https://github.com/FahimFuad/Spike): a powerful game engine which
|
||||
can run on a toaster.
|
||||
|
||||
* Articles, videos and blog posts:
|
||||
* [Some posts](https://skypjack.github.io/tags/#entt) on my personal
|
||||
[blog](https://skypjack.github.io/) are about `EnTT`, for those who want to
|
||||
know **more** on this project.
|
||||
* [Game Engine series](https://www.youtube.com/c/TheChernoProject/videos) by
|
||||
[The Cherno](https://github.com/TheCherno) (not only about `EnTT` but also
|
||||
on the use of an ECS in general):
|
||||
- [Intro to EnTT](https://www.youtube.com/watch?v=D4hz0wEB978).
|
||||
- [Entities and Components](https://www.youtube.com/watch?v=-B1iu4QJTUc).
|
||||
- [The ENTITY Class](https://www.youtube.com/watch?v=GfSzeAcsBb0).
|
||||
- [Camera Systems](https://www.youtube.com/watch?v=ubZn7BlrnTU).
|
||||
- [Scene Camera](https://www.youtube.com/watch?v=UKVFRRufKzo).
|
||||
- [Native Scripting](https://www.youtube.com/watch?v=iIUhg88MK5M).
|
||||
- [Native Scripting (now with virtual functions!)](https://www.youtube.com/watch?v=1cHEcrIn8IQ).
|
||||
- [Scene Hierarchy Panel](https://www.youtube.com/watch?v=wziDnE8guvI).
|
||||
- [Properties Panel](https://www.youtube.com/watch?v=NBpB0qscF3E).
|
||||
- [Camera Component UI](https://www.youtube.com/watch?v=RIMt_6agUiU).
|
||||
- [Drawing Component UI](https://www.youtube.com/watch?v=u3yq8s3KuSE).
|
||||
- [Transform Component UI](https://www.youtube.com/watch?v=8JqcXYbzPJc).
|
||||
- [Adding/Removing Entities and Components UI](https://www.youtube.com/watch?v=PsyGmsIgp9M).
|
||||
- [Saving and Loading Scenes](https://www.youtube.com/watch?v=IEiOP7Y-Mbc).
|
||||
- ... And so on.
|
||||
[Check out](https://www.youtube.com/channel/UCQ-W1KE9EYfdxhL6S4twUNw) the
|
||||
_Game Engine Series_ by The Cherno for more videos.
|
||||
* [Space Battle: Huge edition](http://victor.madtriangles.com/code%20experiment/2018/06/11/post-ecs-battle-huge.html):
|
||||
huge space battle built entirely from scratch.
|
||||
* [Space Battle](https://github.com/vblanco20-1/ECS_SpaceBattle): huge space
|
||||
battle built on `UE4`.
|
||||
* [Experimenting with ECS in UE4](http://victor.madtriangles.com/code%20experiment/2018/03/25/post-ue4-ecs-battle.html):
|
||||
interesting article about `UE4` and `EnTT`.
|
||||
* [Implementing ECS architecture in UE4](https://forums.unrealengine.com/development-discussion/c-gameplay-programming/1449913-implementing-ecs-architecture-in-ue4-giant-space-battle):
|
||||
giant space battle.
|
||||
* [Conan Adventures (SFML and EnTT in C++)](https://leinnan.github.io/blog/conan-adventuressfml-and-entt-in-c.html):
|
||||
create projects in modern C++ using `SFML`, `EnTT`, `Conan` and `CMake`.
|
||||
* [Adding EnTT ECS to Chrysalis](https://www.tauradius.com/post/adding-an-ecs-to-chrysalis/):
|
||||
a blog entry (and its
|
||||
[follow-up](https://www.tauradius.com/post/chrysalis-update-2020-08-02/))
|
||||
about the integration of `EnTT` into `Chrysalis`, an action RPG SDK for
|
||||
CRYENGINE games.
|
||||
* [Creating Minecraft in One Week with C++ and Vulkan](https://vazgriz.com/189/creating-minecraft-in-one-week-with-c-and-vulkan/):
|
||||
a crack at recreating Minecraft in one week using a custom C++ engine and
|
||||
Vulkan ([code included](https://github.com/vazgriz/VoxelGame)).
|
||||
* [Ability Creator](https://www.erichildebrand.net/blog/ability-creator-project-retrospect):
|
||||
project retrospect by [Eric Hildebrand](https://www.erichildebrand.net/).
|
||||
* [EnTT Entity Component System Gaming Library](https://gamefromscratch.com/entt-entity-component-system-gaming-library/):
|
||||
`EnTT` on GameFromScratch.com.
|
||||
|
||||
* Any Other Business:
|
||||
* [ArcGIS Runtime SDKs](https://developers.arcgis.com/arcgis-runtime/) by
|
||||
[Esri](https://www.esri.com/): they use `EnTT` for the internal ECS and the
|
||||
cross platform C++ rendering engine. The SDKs are utilized by a lot of
|
||||
enterprise custom apps, as well as by Esri for its own public applications
|
||||
such as
|
||||
[Explorer](https://play.google.com/store/apps/details?id=com.esri.explorer),
|
||||
[Collector](https://play.google.com/store/apps/details?id=com.esri.arcgis.collector)
|
||||
and
|
||||
[Navigator](https://play.google.com/store/apps/details?id=com.esri.navigator).
|
||||
* [FASTSUITE Edition 2](https://www.fastsuite.com/en_EN/fastsuite/fastsuite-edition-2.html)
|
||||
by [Cenit](http://www.cenit.com/en_EN/about-us/overview.html): they use
|
||||
`EnTT` to drive their simulation, that is, the communication between robot
|
||||
controller emulator and renderer.
|
||||
* [Ragdoll](https://ragdolldynamics.com/): real-time physics for Autodesk Maya
|
||||
2020.
|
||||
* [AtomicDEX](https://github.com/KomodoPlatform/atomicDEX-Desktop): a secure
|
||||
wallet and non-custodial decentralized exchange rolled into one application.
|
||||
* [Apparently](https://www.linkedin.com/in/skypjack/)
|
||||
[NIO](https://www.nio.io/): there was a collaboration to make some changes
|
||||
to `EnTT`, at the time used for internal projects.
|
||||
* [Apparently](https://www.linkedin.com/jobs/view/architekt-c%2B%2B-at-tieto-1219512333/)
|
||||
[Tieto](https://www.tieto.com/): they published a job post where `EnTT` was
|
||||
listed on their software stack.
|
||||
* [Sequentity](https://github.com/alanjfs/sequentity): A MIDI-like
|
||||
sequencer/tracker for C++ and `ImGui` (with `Magnum` and `EnTT`).
|
||||
* [EnTT meets Sol2](https://github.com/skaarj1989/entt-meets-sol2): freely
|
||||
available examples of how to combine `EnTT` and `Sol2`.
|
||||
* [Godot meets EnTT](https://github.com/portaloffreedom/godot_entt_example/):
|
||||
a simple example on how to use `EnTT` within
|
||||
[`Godot`](https://godotengine.org/).
|
||||
* [Godot and GameNetworkingSockets meet EnTT](https://github.com/portaloffreedom/godot_entt_net_example):
|
||||
a simple example on how to use `EnTT` and
|
||||
[`GameNetworkingSockets`](https://github.com/ValveSoftware/GameNetworkingSockets)
|
||||
within [`Godot`](https://godotengine.org/).
|
||||
* [MatchOneEntt](https://github.com/mhaemmerle/MatchOneEntt): port of
|
||||
[Match One](https://github.com/sschmid/Match-One) for `Entitas-CSharp`.
|
||||
* GitHub contains also
|
||||
[many other examples](https://github.com/search?o=desc&q=%22skypjack%2Fentt%22&s=indexed&type=Code)
|
||||
of use of `EnTT` from which to take inspiration if interested.
|
||||
|
||||
If you know of other resources out there that are about `EnTT`, feel free to
|
||||
open an issue or a PR and I'll be glad to add them to this page.
|
||||
75
docs/md/locator.md
Normal file
75
docs/md/locator.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Crash Course: service locator
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Service locator](#service-locator)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
Usually service locators are tightly bound to the services they expose and it's
|
||||
hard to define a general purpose solution. This template based implementation
|
||||
tries to fill the gap and to get rid of the burden of defining a different
|
||||
specific locator for each application.<br/>
|
||||
This class is tiny, partially unsafe and thus risky to use. Moreover it doesn't
|
||||
fit probably most of the scenarios in which a service locator is required. Look
|
||||
at it as a small tool that can sometimes be useful if users know how to handle
|
||||
it.
|
||||
|
||||
# Service locator
|
||||
|
||||
The API is straightforward. The basic idea is that services are implemented by
|
||||
means of interfaces and rely on polymorphism.<br/>
|
||||
The locator is instantiated with the base type of the service if any and a
|
||||
concrete implementation is provided along with all the parameters required to
|
||||
initialize it. As an example:
|
||||
|
||||
```cpp
|
||||
// the service has no base type, a locator is used to treat it as a kind of singleton
|
||||
entt::service_locator<my_service>::set(params...);
|
||||
|
||||
// sets up an opaque service
|
||||
entt::service_locator<audio_interface>::set<audio_implementation>(params...);
|
||||
|
||||
// resets (destroys) the service
|
||||
entt::service_locator<audio_interface>::reset();
|
||||
```
|
||||
|
||||
The locator can also be queried to know if an active service is currently set
|
||||
and to retrieve it if necessary (either as a pointer or as a reference):
|
||||
|
||||
```cpp
|
||||
// no service currently set
|
||||
auto empty = entt::service_locator<audio_interface>::empty();
|
||||
|
||||
// gets a (possibly empty) shared pointer to the service ...
|
||||
std::shared_ptr<audio_interface> ptr = entt::service_locator<audio_interface>::get();
|
||||
|
||||
// ... or a reference, but it's undefined behaviour if the service isn't set yet
|
||||
audio_interface &ref = entt::service_locator<audio_interface>::ref();
|
||||
```
|
||||
|
||||
A common use is to wrap the different locators in a container class, creating
|
||||
aliases for the various services:
|
||||
|
||||
```cpp
|
||||
struct locator {
|
||||
using camera = entt::service_locator<camera_interface>;
|
||||
using audio = entt::service_locator<audio_interface>;
|
||||
// ...
|
||||
};
|
||||
|
||||
// ...
|
||||
|
||||
void init() {
|
||||
locator::camera::set<camera_null>();
|
||||
locator::audio::set<audio_implementation>(params...);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
947
docs/md/meta.md
Normal file
947
docs/md/meta.md
Normal file
@@ -0,0 +1,947 @@
|
||||
# Crash Course: runtime reflection system
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Names and identifiers](#names-and-identifiers)
|
||||
* [Reflection in a nutshell](#reflection-in-a-nutshell)
|
||||
* [Any to the rescue](#any-to-the-rescue)
|
||||
* [Enjoy the runtime](#enjoy-the-runtime)
|
||||
* [Container support](#container-support)
|
||||
* [Pointer-like types](#pointer-like-types)
|
||||
* [Template information](#template-information)
|
||||
* [Implicitly generated default constructor](#implicitly-generated-default-constructor)
|
||||
* [Policies: the more, the less](#policies-the-more-the-less)
|
||||
* [Named constants and enums](#named-constants-and-enums)
|
||||
* [Properties and meta objects](#properties-and-meta-objects)
|
||||
* [Unregister types](#unregister-types)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
Reflection (or rather, its lack) is a trending topic in the C++ world and a tool
|
||||
that can unlock a lot of interesting feature in the specific case of `EnTT`. I
|
||||
looked for a third-party library that met my needs on the subject, but I always
|
||||
came across some details that I didn't like: macros, being intrusive, too many
|
||||
allocations, and so on.<br/>
|
||||
I finally decided to write a built-in, non-intrusive and macro-free runtime
|
||||
reflection system for `EnTT`. Maybe I didn't do better than others or maybe yes,
|
||||
time will tell me, but at least I can model this tool around the library to
|
||||
which it belongs and not the opposite.
|
||||
|
||||
# Names and identifiers
|
||||
|
||||
The meta system doesn't force users to rely on the tools provided by the library
|
||||
when it comes to working with names and identifiers. It does this by offering an
|
||||
API that works with opaque identifiers that may or may not be generated by means
|
||||
of a hashed string.<br/>
|
||||
This means that users can assign any type of identifier to the meta objects, as
|
||||
long as they are numeric. It doesn't matter if they are generated at runtime, at
|
||||
compile-time or with custom functions.
|
||||
|
||||
That being said, the examples in the following sections are all based on the
|
||||
`hashed_string` class as provided by this library. Therefore, where an
|
||||
identifier is required, it's likely that an user defined literal is used as
|
||||
follows:
|
||||
|
||||
```cpp
|
||||
auto factory = entt::meta<my_type>().type("reflected_type"_hs);
|
||||
```
|
||||
|
||||
For what it's worth, this is likely completely equivalent to:
|
||||
|
||||
```cpp
|
||||
auto factory = entt::meta<my_type>().type(42);
|
||||
```
|
||||
|
||||
Obviously, human-readable identifiers are more convenient to use and highly
|
||||
recommended.
|
||||
|
||||
# Reflection in a nutshell
|
||||
|
||||
Reflection always starts from real types (users cannot reflect imaginary types
|
||||
and it would not make much sense, we wouldn't be talking about reflection
|
||||
anymore).<br/>
|
||||
To create a meta node, the library provides the `meta` function that accepts a
|
||||
type to reflect as a template parameter:
|
||||
|
||||
```cpp
|
||||
auto factory = entt::meta<my_type>();
|
||||
```
|
||||
|
||||
This isn't enough to _export_ the given type and make it visible though.<br/>
|
||||
The returned value is a factory object to use to continue building the meta
|
||||
type. In order to make the type _visible_, users can assign it an identifier:
|
||||
|
||||
```cpp
|
||||
auto factory = entt::meta<my_type>().type("reflected_type"_hs);
|
||||
```
|
||||
|
||||
Or use the default one, that is, the built-in identifier for the given type:
|
||||
|
||||
```cpp
|
||||
auto factory = entt::meta<my_type>().type();
|
||||
```
|
||||
|
||||
Identifiers are important because users can retrieve meta types at runtime by
|
||||
searching for them by _name_ other than by type.<br/>
|
||||
On the other hand, there are cases in which users can be interested in adding
|
||||
features to a reflected type so that the reflection system can use it correctly
|
||||
under the hood, but they don't want to also make the type _searchable_. In this
|
||||
case, it's sufficient not to invoke `type`.
|
||||
|
||||
A factory is such that all its member functions return the factory itself or a
|
||||
decorated version of it. This object can be used to add the following:
|
||||
|
||||
* _Constructors_. Actual constructors can be assigned to a reflected type by
|
||||
specifying their list of arguments. Free functions (namely, factories) can be
|
||||
used as well, as long as the return type is the expected one. From a client's
|
||||
point of view, nothing changes if a constructor is a free function or an
|
||||
actual constructor.<br/>
|
||||
Use the `ctor` member function for this purpose:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().ctor<int, char>().ctor<&factory>();
|
||||
```
|
||||
|
||||
* _Destructors_. Free functions can be set as destructors of reflected types.
|
||||
The purpose is to give users the ability to free up resources that require
|
||||
special treatment before an object is actually destroyed.<br/>
|
||||
Use the `dtor` member function for this purpose:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().dtor<&destroy>();
|
||||
```
|
||||
|
||||
A function should neither delete nor explicitly invoke the destructor of a
|
||||
given instance.
|
||||
|
||||
* _Data members_. Both real data members of the underlying type and static and
|
||||
global variables, as well as constants of any kind, can be attached to a meta
|
||||
type. From the point of view of the client, all the variables associated with
|
||||
the reflected type will appear as if they were part of the type itself.<br/>
|
||||
Use the `data` member function for this purpose:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>()
|
||||
.data<&my_type::static_variable>("static"_hs)
|
||||
.data<&my_type::data_member>("member"_hs)
|
||||
.data<&global_variable>("global"_hs);
|
||||
```
|
||||
|
||||
The function requires as an argument the identifier to give to the meta data
|
||||
once created. Users can then access meta data at runtime by searching for them
|
||||
by _name_.<br/>
|
||||
Data members can also be defined by means of a setter and getter pair. Setters
|
||||
and getters can be either free functions, class members or a mix of them, as
|
||||
long as they respect the required signatures. This approach is also convenient
|
||||
to create a read-only variable from a non-const data member:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().data<nullptr, &my_type::data_member>("member"_hs);
|
||||
```
|
||||
|
||||
Refer to the inline documentation for all the details.
|
||||
|
||||
* _Member functions_. Both real member functions of the underlying type and free
|
||||
functions can be attached to a meta type. From the point of view of the
|
||||
client, all the functions associated with the reflected type will appear as if
|
||||
they were part of the type itself.<br/>
|
||||
Use the `func` member function for this purpose:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>()
|
||||
.func<&my_type::static_function>("static"_hs)
|
||||
.func<&my_type::member_function>("member"_hs)
|
||||
.func<&free_function>("free"_hs);
|
||||
```
|
||||
|
||||
The function requires as an argument the identifier to give to the meta
|
||||
function once created. Users can then access meta functions at runtime by
|
||||
searching for them by _name_.<br/>
|
||||
Overloading of meta functions is supported. Overloaded functions are resolved
|
||||
at runtime by the reflection system according to the types of the arguments.
|
||||
|
||||
* _Base classes_. A base class is such that the underlying type is actually
|
||||
derived from it. In this case, the reflection system tracks the relationship
|
||||
and allows for implicit casts at runtime when required.<br/>
|
||||
Use the `base` member function for this purpose:
|
||||
|
||||
```cpp
|
||||
entt::meta<derived_type>().base<base_type>();
|
||||
```
|
||||
|
||||
From now on, wherever a `base_type` is required, an instance of `derived_type`
|
||||
will also be accepted.
|
||||
|
||||
* _Conversion functions_. Actual types can be converted, this is a fact. Just
|
||||
think of the relationship between a `double` and an `int` to see it. Similar
|
||||
to bases, conversion functions allow users to define conversions that will be
|
||||
implicitly performed by the reflection system when required.<br/>
|
||||
Use the `conv` member function for this purpose:
|
||||
|
||||
```cpp
|
||||
entt::meta<double>().conv<int>();
|
||||
```
|
||||
|
||||
That's all, everything users need to create meta types and enjoy the reflection
|
||||
system. At first glance it may not seem that much, but users usually learn to
|
||||
appreciate it over time.<br/>
|
||||
Also, do not forget what these few lines hide under the hood: a built-in,
|
||||
non-intrusive and macro-free system for reflection in C++. Features that are
|
||||
definitely worth the price, at least for me.
|
||||
|
||||
## Any to the rescue
|
||||
|
||||
The reflection system offers a kind of _extended version_ of the `entt::any`
|
||||
class (see the core module for more details).<br/>
|
||||
The purpose is to add some feature on top of those already present, so as to
|
||||
integrate it with the meta type system without having to duplicate the code.
|
||||
|
||||
The API is very similar to that of the `any` type. The class `meta_any` _wraps_
|
||||
many of the feature to infer a meta node, before forwarding some or all of the
|
||||
arguments to the underlying storage.<br/>
|
||||
Among the few relevant differences, `meta_any` adds support for containers and
|
||||
pointer-like types (see the following sections for more details), while `any`
|
||||
does not.<br/>
|
||||
Similar to `any`, this class can also be used to create _aliases_ for unmanaged
|
||||
objects either upon construction using `std::ref` and `std::cref` or from an
|
||||
existing instance by means of the `as_ref` function. However, unlike `any`,
|
||||
`meta_any` treats an empty instance and one initialized with `void` differently:
|
||||
|
||||
```cpp
|
||||
entt::meta_any empty{};
|
||||
entt::meta_any other{std::in_place_type<void>};
|
||||
```
|
||||
|
||||
While `any` considers both as empty, `meta_any` treats objects initialized with
|
||||
`void` as if they were _valid_ ones. This allows to differentiate between failed
|
||||
function calls and function calls that are successful but return nothing.<br/>
|
||||
Finally, the member functions `try_cast`, `cast` and `allow_cast` are used to
|
||||
cast the underlying object to a given type (either a reference or a value type)
|
||||
or to _convert_ a `meta_any` in such a way that a cast becomes viable for the
|
||||
resulting object. There is in fact no `any_cast` equivalent for `meta_any`.
|
||||
|
||||
## Enjoy the runtime
|
||||
|
||||
Once the web of reflected types has been constructed, it's a matter of using it
|
||||
at runtime where required.<br/>
|
||||
All this has the great merit that the reflection system stands in fact as a
|
||||
non-intrusive tool for the runtime, unlike the vast majority of the things
|
||||
offered by this library and closely linked to the compile-time.
|
||||
|
||||
To search for a reflected type there are a few options:
|
||||
|
||||
```cpp
|
||||
// direct access to a reflected type
|
||||
auto by_type = entt::resolve<my_type>();
|
||||
|
||||
// look up a reflected type by identifier
|
||||
auto by_id = entt::resolve("reflected_type"_hs);
|
||||
|
||||
// look up a reflected type by type info
|
||||
auto by_type_id = entt::resolve(entt::type_id<my_type>());
|
||||
```
|
||||
|
||||
There exits also an overload of the `resolve` function to use to iterate all the
|
||||
reflected types at once. It returns an iterable object that can be used in a
|
||||
range-for loop:
|
||||
|
||||
```cpp
|
||||
for(auto type: entt::resolve()) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
In all cases, the returned value is an instance of `meta_type`. This kind of
|
||||
objects offer an API to know their _runtime identifiers_, to iterate all the
|
||||
meta objects associated with them and even to build instances of the underlying
|
||||
type.<br/>
|
||||
Refer to the inline documentation for all the details.
|
||||
|
||||
The meta objects that compose a meta type are accessed in the following ways:
|
||||
|
||||
* _Meta constructors_. They are accessed by types of arguments:
|
||||
|
||||
```cpp
|
||||
auto ctor = entt::resolve<my_type>().ctor<int, char>();
|
||||
```
|
||||
|
||||
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 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_:
|
||||
|
||||
```cpp
|
||||
auto data = entt::resolve<my_type>().data("member"_hs);
|
||||
```
|
||||
|
||||
The returned type is `meta_data` and may be invalid if there is no meta data
|
||||
object associated with the given identifier.<br/>
|
||||
A meta data object offers an API to query the underlying type (for example, to
|
||||
know if it's a const or a static one), to get the meta type of the variable
|
||||
and to set or get the contained value.
|
||||
|
||||
* _Meta functions_. They are accessed by _name_:
|
||||
|
||||
```cpp
|
||||
auto func = entt::resolve<my_type>().func("member"_hs);
|
||||
```
|
||||
|
||||
The returned type is `meta_func` and may be invalid if there is no meta
|
||||
function object associated with the given identifier.<br/>
|
||||
A meta function object offers an API to query the underlying type (for
|
||||
example, to know if it's a const or a static function), to know the number of
|
||||
arguments, the meta return type and the meta types of the parameters. In
|
||||
addition, a meta function object can be used to invoke the underlying function
|
||||
and then get the return value in the form of a `meta_any` object.
|
||||
|
||||
* _Meta bases_. They are accessed through the _name_ of the base types:
|
||||
|
||||
```cpp
|
||||
auto base = entt::resolve<derived_type>().base("base"_hs);
|
||||
```
|
||||
|
||||
The returned type is `meta_type` and may be invalid if there is no meta base
|
||||
object associated with the given identifier.
|
||||
|
||||
All the objects thus obtained as well as the meta types can be explicitly
|
||||
converted to a boolean value to check if they are valid:
|
||||
|
||||
```cpp
|
||||
if(auto func = entt::resolve<my_type>().func("member"_hs); func) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Furthermore, all them are also returned by specific overloads that provide the
|
||||
caller with iterable ranges of top-level elements. As an example:
|
||||
|
||||
```cpp
|
||||
for(auto data = entt::resolve<my_type>().data()) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
A meta type can be used to `construct` actual instances of the underlying
|
||||
type.<br/>
|
||||
In particular, the `construct` member function accepts a variable number of
|
||||
arguments and searches for a match. It then returns a `meta_any` object that may
|
||||
or may not be initialized, depending on whether a suitable constructor has been
|
||||
found or not.
|
||||
|
||||
There is no object that wraps the destructor of a meta type nor a `destroy`
|
||||
member function in its API. Destructors are invoked implicitly by `meta_any`
|
||||
behind the scenes and users have not to deal with them explicitly. Furthermore,
|
||||
they have no name, cannot be searched and wouldn't have member functions to
|
||||
expose anyway.<br/>
|
||||
Similarly, conversion functions aren't directly accessible. They are used
|
||||
internally by `meta_any` and the meta objects when needed.
|
||||
|
||||
Meta types and meta objects in general contain much more than what is said: a
|
||||
plethora of functions in addition to those listed whose purposes and uses go
|
||||
unfortunately beyond the scope of this document.<br/>
|
||||
I invite anyone interested in the subject to look at the code, experiment and
|
||||
read the inline documentation to get the best out of this powerful tool.
|
||||
|
||||
## Container support
|
||||
|
||||
The runtime reflection system also supports containers of all types.<br/>
|
||||
Moreover, _containers_ doesn't necessarily mean those offered by the C++
|
||||
standard library. In fact, user defined data structures can also work with the
|
||||
meta system in many cases.
|
||||
|
||||
To make a container be recognized as such by the meta system, users are required
|
||||
to provide specializations for either the `meta_sequence_container_traits` class
|
||||
or the `meta_associative_container_traits` class, according with the actual type
|
||||
of the container.<br/>
|
||||
`EnTT` already exports the specializations for some common classes. In
|
||||
particular:
|
||||
|
||||
* `std::vector` and `std::array` are exported as _sequence containers_.
|
||||
* `std::map`, `std::set` and their unordered counterparts are exported as
|
||||
_associative containers_.
|
||||
|
||||
It's important to include the header file `container.hpp` to make these
|
||||
specializations available to the compiler when needed.<br/>
|
||||
The same file also contains many examples for the users that are interested in
|
||||
making their own containers available to the meta system.
|
||||
|
||||
When a specialization of the `meta_sequence_container_traits` class exists, the
|
||||
meta system treats the wrapped type as a sequence container. In a similar way,
|
||||
a type is treated as an associative container if a specialization of the
|
||||
`meta_associative_container_traits` class is found for it.<br/>
|
||||
Proxy objects are returned by dedicated members of the `meta_any` class. The
|
||||
following is a deliberately verbose example of how users can access a proxy
|
||||
object for a sequence container:
|
||||
|
||||
```cpp
|
||||
std::vector<int> vec{1, 2, 3};
|
||||
entt::meta_any any{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, 42);
|
||||
```
|
||||
|
||||
This function returns a meta iterator pointing to the inserted element and a
|
||||
boolean value to indicate whether the operation was successful or not. Note
|
||||
that a call to `insert` may silently fail in case of fixed size containers or
|
||||
whether the arguments aren't at least convertible to the required types.<br/>
|
||||
Since the meta iterators are contextually convertible to bool, users can rely
|
||||
on them to know if the operation has failed on the actual container or
|
||||
upstream, for example for an argument conversion problem.
|
||||
|
||||
* The `erase` member function can be used to remove elements from the container.
|
||||
It accepts a meta iterator to the element to remove:
|
||||
|
||||
```cpp
|
||||
auto first = view.begin();
|
||||
// removes the first element from the container
|
||||
view.erase(first);
|
||||
```
|
||||
|
||||
This function returns a meta iterator following the last removed element and a
|
||||
boolean value to indicate whether the operation was successful or not. Note
|
||||
that a call to `erase` may silently fail in case of fixed size containers.
|
||||
|
||||
* The `operator[]` can be used to access elements in a container. It accepts a
|
||||
single argument, that is the position of the element to return:
|
||||
|
||||
```cpp
|
||||
for(std::size_t pos{}, last = view.size(); pos < last; ++pos) {
|
||||
entt::meta_any value = view[pos];
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
The function returns instances of `meta_any` that directly refer to the actual
|
||||
elements. Modifying the returned object will then directly modify the element
|
||||
inside the container.
|
||||
|
||||
Similarly, also the interface of the `meta_associative_container` proxy object
|
||||
is the same for all types of associative containers. However, there are some
|
||||
differences in behavior in the case of key-only containers. In particular:
|
||||
|
||||
* The `key_only` member function returns true if the wrapped container is a
|
||||
key-only one.
|
||||
|
||||
* The `key_type` member function returns the meta type of the keys.
|
||||
|
||||
* The `mapped_type` member function returns an invalid meta type for key-only
|
||||
containers and the meta type of the mapped values for all other types of
|
||||
containers.
|
||||
|
||||
* The `value_type` member function returns the meta type of the elements.<br/>
|
||||
For example, it returns the meta type of `int` for `std::set<int>` while it
|
||||
returns the meta type of `std::pair<const int, char>` for
|
||||
`std::map<int, char>`.
|
||||
|
||||
* The `size` member function returns the number of elements in the container as
|
||||
an unsigned integer value:
|
||||
|
||||
```cpp
|
||||
const auto size = view.size();
|
||||
```
|
||||
|
||||
* The `clear` member function allows to clear the wrapped container and returns
|
||||
true in case of success:
|
||||
|
||||
```cpp
|
||||
const bool ok = view.clear();
|
||||
```
|
||||
|
||||
* The `begin` and `end` member functions return opaque iterators that can be
|
||||
used to iterate the container directly:
|
||||
|
||||
```cpp
|
||||
for(std::pair<entt::meta_any, entt::meta_any> element: view) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
In all cases, given an underlying container of type `C`, the returned element
|
||||
is a key-value pair where the key has type `C::key_type` and the value has
|
||||
type `C::mapped_type`. Since key-only containers don't have a mapped type,
|
||||
their _value_ is nothing more than an invalid `meta_any` object.<br/>
|
||||
All meta iterators are input iterators and don't offer an indirection operator
|
||||
on purpose.
|
||||
|
||||
While the accessed key is usually constant in the associative containers and
|
||||
is therefore returned by copy, the value (if any) is wrapped by an instance of
|
||||
`meta_any` that directly refers to the actual element. Modifying it will then
|
||||
directly modify the element inside the container.
|
||||
|
||||
* The `insert` member function can be used to add elements to the container. It
|
||||
accepts two arguments, respectively the key and the value to be inserted:
|
||||
|
||||
```cpp
|
||||
auto last = view.end();
|
||||
// appends an integer to the container
|
||||
view.insert(last.handle(), 42, 'c');
|
||||
```
|
||||
|
||||
This function returns a boolean value to indicate whether the operation was
|
||||
successful or not. Note that a call to `insert` may fail when the arguments
|
||||
aren't at least convertible to the required types.
|
||||
|
||||
* The `erase` member function can be used to remove elements from the container.
|
||||
It accepts a single argument, that is the key to be removed:
|
||||
|
||||
```cpp
|
||||
view.erase(42);
|
||||
```
|
||||
|
||||
This function returns a boolean value to indicate whether the operation was
|
||||
successful or not. Note that a call to `erase` may fail when the argument
|
||||
isn't at least convertible to the required type.
|
||||
|
||||
* The `operator[]` can be used to access elements in a container. It accepts a
|
||||
single argument, that is the key of the element to return:
|
||||
|
||||
```cpp
|
||||
entt::meta_any value = view[42];
|
||||
```
|
||||
|
||||
The function returns instances of `meta_any` that directly refer to the actual
|
||||
elements. Modifying the returned object will then directly modify the element
|
||||
inside the container.
|
||||
|
||||
Container support is minimal but likely sufficient to satisfy all needs.
|
||||
|
||||
## Pointer-like types
|
||||
|
||||
As with containers, it's also possible to communicate to the meta system which
|
||||
types to consider _pointers_. This will allow to dereference instances of
|
||||
`meta_any`, thus obtaining light _references_ to the pointed objects that are
|
||||
also correctly associated with their meta types.<br/>
|
||||
To make the meta system recognize a type as _pointer-like_, users can specialize
|
||||
the `is_meta_pointer_like` class. `EnTT` already exports the specializations for
|
||||
some common classes. In particular:
|
||||
|
||||
* All types of raw pointers.
|
||||
* `std::unique_ptr` and `std::shared_ptr`.
|
||||
|
||||
It's important to include the header file `pointer.hpp` to make these
|
||||
specializations available to the compiler when needed.<br/>
|
||||
The same file also contains many examples for the users that are interested in
|
||||
making their own pointer-like types available to the meta system.
|
||||
|
||||
When a type is recognized as a pointer-like one by the meta system, it's
|
||||
possible to dereference the instances of `meta_any` that contain these objects.
|
||||
The following is a deliberately verbose example to show how to use this feature:
|
||||
|
||||
```cpp
|
||||
int value = 42;
|
||||
// meta type equivalent to that of int *
|
||||
entt::meta_any any{&value};
|
||||
|
||||
if(any.type().is_pointer_like()) {
|
||||
// meta type equivalent to that of int
|
||||
if(entt::meta_any ref = *any; ref) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Of course, it's not necessary to perform a double check. Instead, it's enough to
|
||||
query the meta type or verify that the returned object is valid. For example,
|
||||
invalid instances are returned when the wrapped object isn't a pointer-like
|
||||
type.<br/>
|
||||
Note that dereferencing a pointer-like object returns an instance of `meta_any`
|
||||
which refers to the pointed object and allows users to modify it directly
|
||||
(unless the returned element is const, of course).
|
||||
|
||||
In general, _dereferencing_ a pointer-like type boils down to a `*ptr`. However,
|
||||
`EnTT` also supports classes that don't offer an `operator*`. In particular:
|
||||
|
||||
* It's possible to exploit a solution based on ADL lookup by offering a function
|
||||
(also a template one) named `dereference_meta_pointer_like`:
|
||||
|
||||
```cpp
|
||||
template<typename Type>
|
||||
Type & dereference_meta_pointer_like(const custom_pointer_type<Type> &ptr) {
|
||||
return ptr.deref();
|
||||
}
|
||||
```
|
||||
|
||||
* When not in control of the type's namespace, it's possible to inject into the
|
||||
`entt` namespace a specialization of the `adl_meta_pointer_like` class
|
||||
template to bypass the adl lookup as a whole:
|
||||
|
||||
```cpp
|
||||
template<typename Type>
|
||||
struct entt::adl_meta_pointer_like<custom_pointer_type<Type>> {
|
||||
static decltype(auto) dereference(const custom_pointer_type<Type> &ptr) {
|
||||
return ptr.deref();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
In all other cases, that is, when dereferencing a pointer works as expected and
|
||||
regardless of the pointed type, no user intervention is required.
|
||||
|
||||
## Template information
|
||||
|
||||
Meta types also provide a minimal set of information about the nature of the
|
||||
original type in case it's a class template.<br/>
|
||||
By default, this works out of the box and requires no user action. However, it's
|
||||
important to include the header file `template.hpp` to make these information
|
||||
available to the compiler when needed.
|
||||
|
||||
Meta template information are easily found:
|
||||
|
||||
```cpp
|
||||
// this method returns true if the type is recognized as a class template specialization
|
||||
if(auto type = entt::resolve<std::shared_ptr<my_type>>(); type.is_template_specialization()) {
|
||||
// meta type of the class template conveniently wrapped by entt::meta_class_template_tag
|
||||
auto class_type = type.template_type();
|
||||
|
||||
// number of template arguments
|
||||
std::size_t arity = type.template_arity();
|
||||
|
||||
// meta type of the i-th argument
|
||||
auto arg_type = type.template_arg(0u);
|
||||
}
|
||||
```
|
||||
|
||||
Typically, when template information for a type are required, what the library
|
||||
provides is sufficient. However, there are some cases where a user may want more
|
||||
details or a different set of information.<br/>
|
||||
Consider the case of a class template that is meant to wrap function types:
|
||||
|
||||
```cpp
|
||||
template<typename>
|
||||
struct function_type;
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
struct function_type<Ret(Args...)> {};
|
||||
```
|
||||
|
||||
In this case, rather than the function type, the user might want the return type
|
||||
and unpacked arguments as if they were different template parameters for the
|
||||
original class template.<br/>
|
||||
To achieve this, users must enter the library internals and provide their own
|
||||
specialization for the class template `entt::meta_template_traits`, such as:
|
||||
|
||||
```cpp
|
||||
template<typename Ret, typename... Args>
|
||||
struct entt::meta_template_traits<function_type<Ret(Args...)>> {
|
||||
using class_type = meta_class_template_tag<function_type>;
|
||||
using args_type = type_list<Ret, Args...>;
|
||||
};
|
||||
```
|
||||
|
||||
The reflection system doesn't verify the accuracy of the information nor infer a
|
||||
correspondence between real types and meta types.<br/>
|
||||
Therefore, the specialization will be used as is and the information it contains
|
||||
will be associated with the appropriate type when required.
|
||||
|
||||
## Implicitly generated default constructor
|
||||
|
||||
In many cases, it's useful to be able to create objects of default constructible
|
||||
types through the reflection system, while not having to explicitly register the
|
||||
meta type or the default constructor.<br/>
|
||||
For example, in the case of primitive types like `int` or `char`, but not just
|
||||
them.
|
||||
|
||||
For this reason and only for default constructible types, default constructors
|
||||
are automatically defined and associated with their meta types, whether they are
|
||||
explicitly or implicitly generated.<br/>
|
||||
Therefore, it won't be necessary to do this in order to construct an integer
|
||||
from its meta type:
|
||||
|
||||
```cpp
|
||||
entt::meta<int>().ctor<>();
|
||||
```
|
||||
|
||||
Instead, just do this:
|
||||
|
||||
```cpp
|
||||
entt::resolve<int>().construct();
|
||||
```
|
||||
|
||||
Where the meta type can be for example the one returned from a meta container,
|
||||
useful for building keys without knowing or having to register the actual types.
|
||||
|
||||
In all cases, when users register custom defaul constructors, they are preferred
|
||||
both during searches and when the `construct` member function is invoked.<br/>
|
||||
However, the implicitly generated default constructor will always be returned,
|
||||
either if one is not explicitly specified or if all constructors are iterated
|
||||
for some reason (in this case, it will always be the last element).
|
||||
|
||||
## Policies: the more, the less
|
||||
|
||||
Policies are a kind of compile-time directives that can be used when registering
|
||||
reflection information.<br/>
|
||||
Their purpose is to require slightly different behavior than the default in some
|
||||
specific cases. For example, when reading a given data member, its value is
|
||||
returned wrapped in a `meta_any` object which, by default, makes a copy of it.
|
||||
For large objects or if the caller wants to access the original instance, this
|
||||
behavior isn't desirable. Policies are there to offer a solution to this and
|
||||
other problems.
|
||||
|
||||
There are a few alternatives available at the moment:
|
||||
|
||||
* The _as-is_ policy, associated with the type `entt::as_is_t`.<br/>
|
||||
This is the default policy. In general, it should never be used explicitly,
|
||||
since it's implicitly selected if no other policy is specified.<br/>
|
||||
In this case, the return values of the functions as well as the properties
|
||||
exposed as data members are always returned by copy in a dedicated wrapper and
|
||||
therefore associated with their original meta types.
|
||||
|
||||
* The _as-void_ policy, associated with the type `entt::as_void_t`.<br/>
|
||||
Its purpose is to discard the return value of a meta object, whatever it is,
|
||||
thus making it appear as if its type were `void`:
|
||||
```cpp
|
||||
entt::meta<my_type>().func<&my_type::member_function, entt::as_void_t>("member"_hs);
|
||||
```
|
||||
If the use with functions is obvious, it must be said that it's also possible
|
||||
to use this policy with constructors and data members. In the first case, the
|
||||
constructor will be invoked but the returned wrapper will actually be empty.
|
||||
In the second case, instead, the property will not be accessible for reading.
|
||||
|
||||
* The _as-ref_ and _as-cref_ policies, associated with the types
|
||||
`entt::as_ref_t` and `entt::as_cref_t`.<br/>
|
||||
They allow to build wrappers that act as references to unmanaged objects.
|
||||
Accessing the object contained in the wrapper for which the _reference_ was
|
||||
requested will make it possible to directly access the instance used to
|
||||
initialize the wrapper itself:
|
||||
```cpp
|
||||
entt::meta<my_type>().data<&my_type::data_member, entt::as_ref_t>("member"_hs);
|
||||
```
|
||||
These policies work with constructors (for example, when objects are taken
|
||||
from an external container rather than created on demand), data members and
|
||||
functions in general.<br/>
|
||||
If on the one hand `as_cref_t` always forces the return type to be const,
|
||||
`as_ref_t` _adapts_ to the constness of the passed object and to that of the
|
||||
return type if any.
|
||||
|
||||
Some uses are rather trivial, but it's useful to note that there are some less
|
||||
obvious corner cases that can in turn be solved with the use of policies.
|
||||
|
||||
## Named constants and enums
|
||||
|
||||
A special mention should be made for constant values and enums. It wouldn't be
|
||||
necessary, but it will help distracted readers.
|
||||
|
||||
As mentioned, the `data` member function can be used to reflect constants of any
|
||||
type among the other things.<br/>
|
||||
This allows users to create meta types for enums that will work exactly like any
|
||||
other meta type built from a class. Similarly, arithmetic types can be enriched
|
||||
with constants of special meaning where required.<br/>
|
||||
Personally, I find it very useful not to export what is the difference between
|
||||
enums and classes in C++ directly in the space of the reflected types.
|
||||
|
||||
All the values thus exported will appear to users as if they were constant data
|
||||
members of the reflected types.
|
||||
|
||||
Exporting constant values or elements from an enum is as simple as ever:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_enum>()
|
||||
.data<my_enum::a_value>("a_value"_hs)
|
||||
.data<my_enum::another_value>("another_value"_hs);
|
||||
|
||||
entt::meta<int>().data<2048>("max_int"_hs);
|
||||
```
|
||||
|
||||
It goes without saying that accessing them is trivial as well. It's a matter of
|
||||
doing the following, as with any other data member of a meta type:
|
||||
|
||||
```cpp
|
||||
auto value = entt::resolve<my_enum>().data("a_value"_hs).get({}).cast<my_enum>();
|
||||
auto max = entt::resolve<int>().data("max_int"_hs).get({}).cast<int>();
|
||||
```
|
||||
|
||||
As a side note, remember that all this happens behind the scenes without any
|
||||
allocation because of the small object optimization performed by the `meta_any`
|
||||
class.
|
||||
|
||||
## Properties and meta objects
|
||||
|
||||
Sometimes (for example, when it comes to creating an editor) it might be useful
|
||||
to attach properties to the meta objects created. Fortunately, this is possible
|
||||
for most of them.<br/>
|
||||
For the meta objects that support properties, the member functions of the
|
||||
factory used for registering them will return a decorated version of the factory
|
||||
itself. The latter can be used to attach properties to the last created meta
|
||||
object.<br/>
|
||||
Apparently, it's more difficult to say than to do:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().type("reflected_type"_hs).prop("tooltip"_hs, "message");
|
||||
```
|
||||
|
||||
Properties are always in the key/value form. There are no restrictions on the
|
||||
type of the key or value, as long as they are copy constructible objects.<br/>
|
||||
Multiple formats are supported when it comes to defining a property:
|
||||
|
||||
* Properties as key/value pairs:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().type("reflected_type"_hs).prop("tooltip"_hs, "message");
|
||||
```
|
||||
|
||||
* Properties as `std::pair`s:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().type("reflected_type"_hs).prop(std::make_pair("tooltip"_hs, "message"));
|
||||
```
|
||||
|
||||
* Key only properties:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().type("reflected_type"_hs).prop(my_enum::key_only);
|
||||
```
|
||||
|
||||
* Properties as `std::tuple`s:
|
||||
|
||||
```cpp
|
||||
entt::meta<my_type>().type("reflected_type"_hs).prop(std::make_tuple(std::make_pair("tooltip"_hs, "message"), my_enum::key_only));
|
||||
```
|
||||
|
||||
A tuple contains one or more properties. All of them are treated individually.
|
||||
|
||||
* 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
|
||||
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
|
||||
for(auto prop: entt::resolve<my_type>().prop()) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// search for a given property by name
|
||||
auto prop = entt::resolve<my_type>().prop("tooltip"_hs);
|
||||
```
|
||||
|
||||
Meta properties are objects having a fairly poor interface, all in all. They
|
||||
only provide the `key` and the `value` member functions to be used to retrieve
|
||||
the key and the value contained in the form of `meta_any` objects, respectively.
|
||||
|
||||
## Unregister types
|
||||
|
||||
A type registered with the reflection system can also be unregistered. This
|
||||
means unregistering all its data members, member functions, conversion functions
|
||||
and so on. However, base classes aren't unregistered as well, since they don't
|
||||
necessarily depend on it. Similarly, implicitly generated types (as an example,
|
||||
the meta types implicitly generated for function parameters when needed) aren't
|
||||
unregistered.<br/>
|
||||
Roughly speaking, unregistering a type means disconnecting all associated meta
|
||||
objects from it and making its identifier no longer visible. The underlying node
|
||||
will remain available though, as if it were implicitly generated:
|
||||
|
||||
```cpp
|
||||
entt::resolve<my_type>().reset();
|
||||
```
|
||||
|
||||
The type can be re-registered later with a completely different name and form.
|
||||
359
docs/md/poly.md
Normal file
359
docs/md/poly.md
Normal file
@@ -0,0 +1,359 @@
|
||||
# Crash Course: poly
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Other libraries](#other-libraries)
|
||||
* [Concept and implementation](#concept-and-implementation)
|
||||
* [Deduced interface](#deduced-interface)
|
||||
* [Defined interface](#defined-interface)
|
||||
* [Fullfill a concept](#fullfill-a-concept)
|
||||
* [Inheritance](#inheritance)
|
||||
* [Static polymorphism in the wild](#static-polymorphism-in-the-wild)
|
||||
* [Storage size and alignment requirement](#storage-size-and-alignment-requirement)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
Static polymorphism is a very powerful tool in C++, albeit sometimes cumbersome
|
||||
to obtain.<br/>
|
||||
This module aims to make it simple and easy to use.
|
||||
|
||||
The library allows to define _concepts_ as interfaces to fullfill with concrete
|
||||
classes withouth having to inherit from a common base.<br/>
|
||||
This is, among others, one of the advantages of static polymorphism in general
|
||||
and of a generic wrapper like that offered by the `poly` class template in
|
||||
particular.<br/>
|
||||
What users get is an object that can be passed around as such and not through a
|
||||
reference or a pointer, as happens when it comes to working with dynamic
|
||||
polymorphism.
|
||||
|
||||
Since the `poly` class template makes use of `entt::any` internally, it also
|
||||
supports most of its feature. Among the most important, the possibility to
|
||||
create aliases to existing and thus unmanaged objects. This allows users to
|
||||
exploit the static polymorphism while maintaining ownership of objects.<br/>
|
||||
Likewise, the `poly` class template also benefits from the small buffer
|
||||
optimization offered by the `entt::any` class and therefore minimizes the number
|
||||
of allocations, avoiding them altogether where possible.
|
||||
|
||||
## Other libraries
|
||||
|
||||
There are some very interesting libraries regarding static polymorphism.<br/>
|
||||
Among all, the two that I prefer are:
|
||||
|
||||
* [`dyno`](https://github.com/ldionne/dyno): runtime polymorphism done right.
|
||||
* [`Poly`](https://github.com/facebook/folly/blob/master/folly/docs/Poly.md):
|
||||
a class template that makes it easy to define a type-erasing polymorphic
|
||||
object wrapper.
|
||||
|
||||
The former is admittedly an experimental library, with many interesting ideas.
|
||||
I've some doubts about the usefulness of some feature in real world projects,
|
||||
but perhaps my lack of experience comes into play here. In my opinion, its only
|
||||
flaw is the API which I find slightly more cumbersome than other solutions.<br/>
|
||||
The latter was undoubtedly a source of inspiration for this module, although I
|
||||
opted for different choices in the implementation of both the final API and some
|
||||
feature.
|
||||
|
||||
Either way, the authors are gurus of the C++ community, people I only have to
|
||||
learn from.
|
||||
|
||||
# Concept and implementation
|
||||
|
||||
The first thing to do to create a _type-erasing polymorphic object wrapper_ (to
|
||||
use the terminology introduced by Eric Niebler) is to define a _concept_ that
|
||||
types will have to adhere to.<br/>
|
||||
For this purpose, the library offers a single class that supports both deduced
|
||||
and fully defined interfaces. Although having interfaces deduced automatically
|
||||
is convenient and allows users to write less code in most cases, this has some
|
||||
limitations and it's therefore useful to be able to get around the deduction by
|
||||
providing a custom definition for the static virtual table.
|
||||
|
||||
Once the interface is defined, it will be sufficient to provide a generic
|
||||
implementation to fullfill the concept.<br/>
|
||||
Also in this case, the library allows customizations based on types or families
|
||||
of types, so as to be able to go beyond the generic case where necessary.
|
||||
|
||||
## Deduced interface
|
||||
|
||||
This is how a concept with a deduced interface is introduced:
|
||||
|
||||
```cpp
|
||||
struct Drawable: entt::type_list<> {
|
||||
template<typename Base>
|
||||
struct type: Base {
|
||||
void draw() { this->template invoke<0>(*this); }
|
||||
};
|
||||
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
It's recognizable by the fact that it inherits from an empty type list.<br/>
|
||||
Functions can also be const, accept any number of paramters and return a type
|
||||
other than `void`:
|
||||
|
||||
```cpp
|
||||
struct Drawable: entt::type_list<> {
|
||||
template<typename Base>
|
||||
struct type: Base {
|
||||
bool draw(int pt) const { return this->template invoke<0>(*this, pt); }
|
||||
};
|
||||
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
In this case, all parameters must be passed to `invoke` after the reference to
|
||||
`this` and the return value is whatever the internal call returns.<br/>
|
||||
As for `invoke`, this is a name that is injected into the _concept_ through
|
||||
`Base`, from which one must necessarily inherit. Since it's also a dependent
|
||||
name, the `this-> template` form is unfortunately necessary due to the rules of
|
||||
the language. However, there exists also an alternative that goes through an
|
||||
external call:
|
||||
|
||||
```cpp
|
||||
struct Drawable: entt::type_list<> {
|
||||
template<typename Base>
|
||||
struct type: Base {
|
||||
bool draw() const { entt::poly_call<0>(*this); }
|
||||
};
|
||||
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
Once the _concept_ is defined, users must provide a generic implementation of it
|
||||
in order to tell the system how any type can satisfy its requirements. This is
|
||||
done via an alias template within the concept itself.<br/>
|
||||
The index passed as a template parameter to either `invoke` or `poly_call`
|
||||
refers to how this alias is defined.
|
||||
|
||||
## Defined interface
|
||||
|
||||
A fully defined concept is no different to one for which the interface is
|
||||
deduced, with the only difference that the list of types is not empty this time:
|
||||
|
||||
```cpp
|
||||
struct Drawable: entt::type_list<void()> {
|
||||
template<typename Base>
|
||||
struct type: Base {
|
||||
void draw() { entt::poly_call<0>(*this); }
|
||||
};
|
||||
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
Again, parameters and return values other than `void` are allowed. Also, the
|
||||
function type must be const when the method to bind to it is const:
|
||||
|
||||
```cpp
|
||||
struct Drawable: entt::type_list<bool(int) const> {
|
||||
template<typename Base>
|
||||
struct type: Base {
|
||||
bool draw(int pt) const { return entt::poly_call<0>(*this, pt); }
|
||||
};
|
||||
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
Why should a user fully define a concept if the function types are the same as
|
||||
the deduced ones?<br>
|
||||
Because, in fact, this is exactly the limitation that can be worked around by
|
||||
manually defining the static virtual table.
|
||||
|
||||
When things are deduced, there is an implicit constraint.<br/>
|
||||
If the concept exposes a member function called `draw` with function type
|
||||
`void()`, a concept can be satisfied:
|
||||
|
||||
* Either by a class that exposes a member function with the same name and the
|
||||
same signature.
|
||||
|
||||
* Or through a lambda that makes use of existing member functions from the
|
||||
interface itself.
|
||||
|
||||
In other words, it's not possible to make use of functions not belonging to the
|
||||
interface, even if they are present in the types that fulfill the concept.<br/>
|
||||
Similarly, it's not possible to deduce a function in the static virtual table
|
||||
with a function type different from that of the associated member function in
|
||||
the interface itself.
|
||||
|
||||
Explicitly defining a static virtual table suppresses the deduction step and
|
||||
allows maximum flexibility when providing the implementation for a concept.
|
||||
|
||||
## Fullfill a concept
|
||||
|
||||
The `impl` alias template of a concept is used to define how it's fulfilled:
|
||||
|
||||
```cpp
|
||||
struct Drawable: entt::type_list<> {
|
||||
// ...
|
||||
|
||||
template<typename Type>
|
||||
using impl = entt::value_list<&Type::draw>;
|
||||
};
|
||||
```
|
||||
|
||||
In this case, it's stated that the `draw` method of a generic type will be
|
||||
enough to satisfy the requirements of the `Drawable` concept.<br/>
|
||||
Both member functions and free functions are supported to fullfill concepts:
|
||||
|
||||
```cpp
|
||||
template<typename Type>
|
||||
void print(Type &self) { self.print(); }
|
||||
|
||||
struct Drawable: entt::type_list<void()> {
|
||||
// ...
|
||||
|
||||
template<typename Type>
|
||||
using impl = entt::value_list<&print<Type>>;
|
||||
};
|
||||
```
|
||||
|
||||
Likewise, as long as the parameter types and return type support conversions to
|
||||
and from those of the function type referenced in the static virtual table, the
|
||||
actual implementation may differ in its function type since it's erased
|
||||
internally.<br/>
|
||||
Moreover, the `self` parameter isn't strictly required by the system and can be
|
||||
left out for free functions if not required.
|
||||
|
||||
Refer to the inline documentation for more details.
|
||||
|
||||
# Inheritance
|
||||
|
||||
_Concept inheritance_ is straightforward due to how poly looks like in `EnTT`.
|
||||
Therefore, it's quite easy to build hierarchies of concepts if necessary.<br/>
|
||||
The only constraint is that all concepts in a hierarchy must belong to the same
|
||||
_family_, that is, they must be either all deduced or all defined.
|
||||
|
||||
For a deduced concept, inheritance is achieved in a few steps:
|
||||
|
||||
```cpp
|
||||
struct DrawableAndErasable: entt::type_list<> {
|
||||
template<typename Base>
|
||||
struct type: typename Drawable::template type<Base> {
|
||||
static constexpr auto base = std::tuple_size_v<typename entt::poly_vtable<Drawable>::type>;
|
||||
void erase() { entt::poly_call<base + 0>(*this); }
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
using impl = entt::value_list_cat_t<
|
||||
typename Drawable::impl<Type>,
|
||||
entt::value_list<&Type::erase>
|
||||
>;
|
||||
};
|
||||
```
|
||||
|
||||
The static virtual table is empty and must remain so.<br/>
|
||||
On the other hand, `type` no longer inherits from `Base` and instead forwards
|
||||
its template parameter to the type exposed by the _base class_. Internally, the
|
||||
size of the static virtual table of the base class is used as an offset for the
|
||||
local indexes.<br/>
|
||||
Finally, by means of the `value_list_cat_t` utility, the implementation consists
|
||||
in appending the new functions to the previous list.
|
||||
|
||||
As for a defined concept instead, also the list of types must be extended, in a
|
||||
similar way to what is shown for the implementation of the above concept.<br/>
|
||||
To do this, it's useful to declare a function that allows to convert a _concept_
|
||||
into its underlying `type_list` object:
|
||||
|
||||
```cpp
|
||||
template<typename... Type>
|
||||
entt::type_list<Type...> as_type_list(const entt::type_list<Type...> &);
|
||||
```
|
||||
|
||||
The definition isn't strictly required, since the function will only be used
|
||||
through a `decltype` as it follows:
|
||||
|
||||
```cpp
|
||||
struct DrawableAndErasable: entt::type_list_cat_t<
|
||||
decltype(as_type_list(std::declval<Drawable>())),
|
||||
entt::type_list<void()>
|
||||
> {
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
Similar to above, `type_list_cat_t` is used to concatenate the underlying static
|
||||
virtual table with the new function types.<br/>
|
||||
Everything else is the same as already shown instead.
|
||||
|
||||
# Static polymorphism in the wild
|
||||
|
||||
Once the _concept_ and implementation have been introduced, it will be possible
|
||||
to use the `poly` class template to contain instances that meet the
|
||||
requirements:
|
||||
|
||||
```cpp
|
||||
using drawable = entt::poly<Drawable>;
|
||||
|
||||
struct circle {
|
||||
void draw() { /* ... */ }
|
||||
};
|
||||
|
||||
struct square {
|
||||
void draw() { /* ... */ }
|
||||
};
|
||||
|
||||
// ...
|
||||
|
||||
drawable instance{circle{}};
|
||||
instance->draw();
|
||||
|
||||
instance = square{};
|
||||
instance->draw();
|
||||
```
|
||||
|
||||
The `poly` class template offers a wide range of constructors, from the default
|
||||
one (which will return an uninitialized `poly` object) to the copy and move
|
||||
constructor, as well as the ability to create objects in-place.<br/>
|
||||
Among others, there is a constructor that allows users to wrap unmanaged objects
|
||||
in a `poly` instance (either const or non-const ones):
|
||||
|
||||
```cpp
|
||||
circle shape;
|
||||
drawable instance{std::ref(shape)};
|
||||
```
|
||||
|
||||
Similarly, it's possible to create non-owning copies of `poly` from an existing
|
||||
object:
|
||||
|
||||
```cpp
|
||||
drawable other = instance.as_ref();
|
||||
```
|
||||
|
||||
In both cases, although the interface of the `poly` object doesn't change, it
|
||||
won't construct any element or take care of destroying the referenced objects.
|
||||
|
||||
Note also how the underlying concept is accessed via a call to `operator->` and
|
||||
not directly as `instance.draw()`.<br/>
|
||||
This allows users to decouple the API of the wrapper from that of the concept.
|
||||
Therefore, where `instance.data()` will invoke the `data` member function of the
|
||||
poly object, `instance->data()` will map directly to the functionality exposed
|
||||
by the underlying concept.
|
||||
|
||||
# Storage size and alignment requirement
|
||||
|
||||
Under the hood, the `poly` class template makes use of `entt::any`. Therefore,
|
||||
it can take advantage of the possibility of defining at compile-time the size of
|
||||
the storage suitable for the small buffer optimization as well as the alignment
|
||||
requirements:
|
||||
|
||||
```cpp
|
||||
entt::basic_poly<Drawable, sizeof(double[4]), alignof(double[4])>
|
||||
```
|
||||
|
||||
The default size is `sizeof(double[2])`, which seems like a good compromise
|
||||
between a buffer that is too large and one unable to hold anything larger than
|
||||
an integer. The alignment requirement is optional instead and by default such
|
||||
that it's the most stringent (the largest) for any object whose size is at most
|
||||
equal to the one provided.<br/>
|
||||
It's worth noting that providing a size of 0 (which is an accepted value in all
|
||||
respects) will force the system to dynamically allocate the contained objects in
|
||||
all cases.
|
||||
212
docs/md/process.md
Normal file
212
docs/md/process.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# Crash Course: cooperative scheduler
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [The process](#the-process)
|
||||
* [Adaptor](#adaptor)
|
||||
* [The scheduler](#the-scheduler)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
Sometimes processes are a useful tool to work around the strict definition of a
|
||||
system and introduce logic in a different way, usually without resorting to the
|
||||
introduction of other components.
|
||||
|
||||
`EnTT` offers a minimal support to this paradigm by introducing a few classes
|
||||
that users can use to define and execute cooperative processes.
|
||||
|
||||
# The process
|
||||
|
||||
A typical process must inherit from the `process` class template that stays true
|
||||
to the CRTP idiom. Moreover, derived classes must specify what's the intended
|
||||
type for elapsed times.
|
||||
|
||||
A process should expose publicly the following member functions whether
|
||||
required (note that it isn't required to define a function unless the derived
|
||||
class wants to _override_ the default behavior):
|
||||
|
||||
* `void update(Delta, void *);`
|
||||
|
||||
It's invoked once per tick until a process is explicitly aborted or it
|
||||
terminates either with or without errors. Even though it's not mandatory to
|
||||
declare this member function, as a rule of thumb each process should at
|
||||
least define it to work properly. The `void *` parameter is an opaque pointer
|
||||
to user data (if any) forwarded directly to the process during an update.
|
||||
|
||||
* `void init();`
|
||||
|
||||
It's invoked when the process joins the running queue of a scheduler. This
|
||||
happens as soon as it's attached to the scheduler if the process is a top
|
||||
level one, otherwise when it replaces its parent if the process is a
|
||||
continuation.
|
||||
|
||||
* `void succeeded();`
|
||||
|
||||
It's invoked in case of success, immediately after an update and during the
|
||||
same tick.
|
||||
|
||||
* `void failed();`
|
||||
|
||||
It's invoked in case of errors, immediately after an update and during the
|
||||
same tick.
|
||||
|
||||
* `void aborted();`
|
||||
|
||||
It's invoked only if a process is explicitly aborted. There is no guarantee
|
||||
that it executes in the same tick, this depends solely on whether the
|
||||
process is aborted immediately or not.
|
||||
|
||||
Derived classes can also change the internal state of a process by invoking
|
||||
`succeed` and `fail`, as well as `pause` and `unpause` the process itself. All
|
||||
these are protected member functions made available to be able to manage the
|
||||
life cycle of a process from a derived class.
|
||||
|
||||
Here is a minimal example for the sake of curiosity:
|
||||
|
||||
```cpp
|
||||
struct my_process: entt::process<my_process, std::uint32_t> {
|
||||
using delta_type = std::uint32_t;
|
||||
|
||||
my_process(delta_type delay)
|
||||
: remaining{delay}
|
||||
{}
|
||||
|
||||
void update(delta_type delta, void *) {
|
||||
remaining -= std::min(remaining, delta);
|
||||
|
||||
// ...
|
||||
|
||||
if(!remaining) {
|
||||
succeed();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
delta_type remaining;
|
||||
};
|
||||
```
|
||||
|
||||
## Adaptor
|
||||
|
||||
Lambdas and functors can't be used directly with a scheduler for they are not
|
||||
properly defined processes with managed life cycles.<br/>
|
||||
This class helps in filling the gap and turning lambdas and functors into
|
||||
full featured processes usable by a scheduler.
|
||||
|
||||
The function call operator has a signature similar to the one of the `update`
|
||||
function of a process but for the fact that it receives two extra arguments to
|
||||
call whenever a process is terminated with success or with an error:
|
||||
|
||||
```cpp
|
||||
void(Delta delta, void *data, auto succeed, auto fail);
|
||||
```
|
||||
|
||||
Parameters have the following meaning:
|
||||
|
||||
* `delta` is the elapsed time.
|
||||
* `data` is an opaque pointer to user data if any, `nullptr` otherwise.
|
||||
* `succeed` is a function to call when a process terminates with success.
|
||||
* `fail` is a function to call when a process terminates with errors.
|
||||
|
||||
Both `succeed` and `fail` accept no parameters at all.
|
||||
|
||||
Note that usually users shouldn't worry about creating adaptors at all. A
|
||||
scheduler creates them internally each and every time a lambda or a functor is
|
||||
used as a process.
|
||||
|
||||
# The scheduler
|
||||
|
||||
A cooperative scheduler runs different processes and helps managing their life
|
||||
cycles.
|
||||
|
||||
Each process is invoked once per tick. If it terminates, it's removed
|
||||
automatically from the scheduler and it's never invoked again. Otherwise it's
|
||||
a good candidate to run one more time the next tick.<br/>
|
||||
A process can also have a child. In this case, the parent process is replaced
|
||||
with its child when it terminates and only if it returns with success. In case
|
||||
of errors, both the parent process and its child are discarded. This way, it's
|
||||
easy to create chain of processes to run sequentially.
|
||||
|
||||
Using a scheduler is straightforward. To create it, users must provide only the
|
||||
type for the elapsed times and no arguments at all:
|
||||
|
||||
```cpp
|
||||
entt::scheduler<std::uint32_t> scheduler;
|
||||
```
|
||||
|
||||
It has member functions to query its internal data structures, like `empty` or
|
||||
`size`, as well as a `clear` utility to reset it to a clean state:
|
||||
|
||||
```cpp
|
||||
// checks if there are processes still running
|
||||
const auto empty = scheduler.empty();
|
||||
|
||||
// gets the number of processes still running
|
||||
entt::scheduler<std::uint32_t>::size_type size = scheduler.size();
|
||||
|
||||
// resets the scheduler to its initial state and discards all the processes
|
||||
scheduler.clear();
|
||||
```
|
||||
|
||||
To attach a process to a scheduler there are mainly two ways:
|
||||
|
||||
* If the process inherits from the `process` class template, it's enough to
|
||||
indicate its type and submit all the parameters required to construct it to
|
||||
the `attach` member function:
|
||||
|
||||
```cpp
|
||||
scheduler.attach<my_process>(1000u);
|
||||
```
|
||||
|
||||
* Otherwise, in case of a lambda or a functor, it's enough to provide an
|
||||
instance of the class to the `attach` member function:
|
||||
|
||||
```cpp
|
||||
scheduler.attach([](auto...){ /* ... */ });
|
||||
```
|
||||
|
||||
In both cases, the return value is an opaque object that offers a `then` member
|
||||
function to use to create chains of processes to run sequentially.<br/>
|
||||
As a minimal example of use:
|
||||
|
||||
```cpp
|
||||
// schedules a task in the form of a lambda function
|
||||
scheduler.attach([](auto delta, void *, auto succeed, auto fail) {
|
||||
// ...
|
||||
})
|
||||
// appends a child in the form of another lambda function
|
||||
.then([](auto delta, void *, auto succeed, auto fail) {
|
||||
// ...
|
||||
})
|
||||
// appends a child in the form of a process class
|
||||
.then<my_process>(1000u);
|
||||
```
|
||||
|
||||
To update a scheduler and therefore all its processes, the `update` member
|
||||
function is the way to go:
|
||||
|
||||
```cpp
|
||||
// updates all the processes, no user data are provided
|
||||
scheduler.update(delta);
|
||||
|
||||
// updates all the processes and provides them with custom data
|
||||
scheduler.update(delta, &data);
|
||||
```
|
||||
|
||||
In addition to these functions, the scheduler offers an `abort` member function
|
||||
that can be used to discard all the running processes at once:
|
||||
|
||||
```cpp
|
||||
// aborts all the processes abruptly ...
|
||||
scheduler.abort(true);
|
||||
|
||||
// ... or gracefully during the next tick
|
||||
scheduler.abort();
|
||||
```
|
||||
63
docs/md/reference.md
Normal file
63
docs/md/reference.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Similar projects
|
||||
|
||||
There are many projects similar to `EnTT`, both open source and not.<br/>
|
||||
Some even borrowed some ideas from this library and expressed them in different
|
||||
languages.<br/>
|
||||
Others developed different architectures from scratch and therefore offer
|
||||
alternative solutions with their pros and cons.
|
||||
|
||||
Below an incomplete list of those that I've come across so far.<br/>
|
||||
If some terms or designs aren't clear, I recommend referring to the
|
||||
[_ECS Back and Forth_](https://skypjack.github.io/tags/#ecs) series for all the
|
||||
details.
|
||||
|
||||
I hope this list can grow much more in the future:
|
||||
|
||||
* C:
|
||||
* [destral_ecs](https://github.com/roig/destral_ecs): a single-file ECS based
|
||||
on sparse sets.
|
||||
* [Diana](https://github.com/discoloda/Diana): an ECS that uses sparse sets to
|
||||
keep track of entities in systems.
|
||||
* [Flecs](https://github.com/SanderMertens/flecs): a multithreaded archetype
|
||||
ECS based on semi-contiguous arrays rather than chunks.
|
||||
* [lent](https://github.com/nem0/lent): the Donald Trump of the ECS libraries.
|
||||
|
||||
* C++:
|
||||
* [decs](https://github.com/vblanco20-1/decs): a chunk based archetype ECS.
|
||||
* [ecst](https://github.com/SuperV1234/ecst): a multithreaded compile-time
|
||||
ECS that uses sparse sets to keep track of entities in systems.
|
||||
* [EntityX](https://github.com/alecthomas/entityx): a bitset based ECS that
|
||||
uses a single large matrix of components indexed with entities.
|
||||
* [Polypropylene](https://github.com/pmbittner/Polypropylene): a hybrid
|
||||
solution between an ECS and dynamic mixins.
|
||||
|
||||
* C#
|
||||
* [Entitas](https://github.com/sschmid/Entitas-CSharp): the ECS framework for
|
||||
C# and Unity, where _reactive systems_ were invented.
|
||||
* [LeoECS](https://github.com/Leopotam/ecs): simple lightweight C# Entity
|
||||
Component System framework.
|
||||
* [Svelto.ECS](https://github.com/sebas77/Svelto.ECS): a very interesting
|
||||
platform agnostic and table based ECS framework.
|
||||
|
||||
* Go:
|
||||
* [gecs](https://github.com/tutumagi/gecs): a sparse sets based ECS inspired
|
||||
by `EnTT`.
|
||||
|
||||
* Javascript:
|
||||
* [\@javelin/ecs](https://github.com/3mcd/javelin/tree/master/packages/ecs): an
|
||||
archetype ECS in TypeScript.
|
||||
* [ecsy](https://github.com/MozillaReality/ecsy): I haven't had the time to
|
||||
investigate the underlying design of `ecsy` but it looks cool anyway.
|
||||
|
||||
* Rust:
|
||||
* [Legion](https://github.com/TomGillen/legion): a chunk based archetype ECS.
|
||||
* [Shipyard](https://github.com/leudz/shipyard): it borrows some ideas from
|
||||
`EnTT` and offers a sparse sets based ECS with grouping functionalities.
|
||||
* [Specs](https://github.com/amethyst/specs): a parallel ECS based mainly on
|
||||
hierarchical bitsets that allows different types of storage as needed.
|
||||
|
||||
* Zig
|
||||
* [zig-ecs](https://github.com/prime31/zig-ecs): a _zig-ification_ of `EnTT`.
|
||||
|
||||
If you know of other resources out there that can be of interest for the reader,
|
||||
feel free to open an issue or a PR and I'll be glad to add them to this page.
|
||||
231
docs/md/resource.md
Normal file
231
docs/md/resource.md
Normal file
@@ -0,0 +1,231 @@
|
||||
# Crash Course: resource management
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [The resource, the loader and the cache](#the-resource-the-loader-and-the-cache)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
Resource management is usually one of the most critical part of a software like
|
||||
a game. Solutions are often tuned to the particular application. There exist
|
||||
several approaches and all of them are perfectly fine as long as they fit the
|
||||
requirements of the piece of software in which they are used.<br/>
|
||||
Examples are loading everything on start, loading on request, predictive
|
||||
loading, and so on.
|
||||
|
||||
`EnTT` doesn't pretend to offer a _one-fits-all_ solution for the different
|
||||
cases. Instead, it offers a minimal and perhaps trivial cache that can be useful
|
||||
most of the time during prototyping and sometimes even in a production
|
||||
environments.<br/>
|
||||
For those interested in the subject, the plan is to improve it considerably over
|
||||
time in terms of performance, memory usage and functionalities. Hoping to make
|
||||
it, of course, one step at a time.
|
||||
|
||||
# The resource, the loader and the cache
|
||||
|
||||
There are three main actors in the model: the resource, the loader and the
|
||||
cache.
|
||||
|
||||
The _resource_ is whatever users want it to be. An image, a video, an audio,
|
||||
whatever. There are no limits.<br/>
|
||||
As a minimal example:
|
||||
|
||||
```cpp
|
||||
struct my_resource { const int value; };
|
||||
```
|
||||
|
||||
A _loader_ is a class the aim of which is to load a specific resource. It has to
|
||||
inherit directly from a dedicated base class as in the following example:
|
||||
|
||||
```cpp
|
||||
struct my_loader final: entt::resource_loader<my_loader, my_resource> {
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
Where `my_resource` is the type of resources it creates.<br/>
|
||||
A resource loader must also expose a public const member function named `load`
|
||||
that accepts a variable number of arguments and returns a shared pointer to a
|
||||
resource.<br/>
|
||||
As an example:
|
||||
|
||||
```cpp
|
||||
struct my_loader: entt::resource_loader<my_loader, my_resource> {
|
||||
std::shared_ptr<my_resource> load(int value) const {
|
||||
// ...
|
||||
return std::shared_ptr<my_resource>(new my_resource{ value });
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
In general, resource loaders should not have a state or retain data of any type.
|
||||
They should let the cache manage their resources instead.<br/>
|
||||
As a side note, base class and CRTP idiom aren't strictly required with the
|
||||
current implementation. One could argue that a cache can easily work with
|
||||
loaders of any type. However, future changes won't be breaking ones by forcing
|
||||
the use of a base class today and that's why the model is already in its place.
|
||||
|
||||
Finally, a cache is a specialization of a class template tailored to a specific
|
||||
resource:
|
||||
|
||||
```cpp
|
||||
using my_cache = entt::resource_cache<my_resource>;
|
||||
|
||||
// ...
|
||||
|
||||
my_cache cache{};
|
||||
```
|
||||
|
||||
The idea is to create different caches for different types of resources and to
|
||||
manage each one independently in the most appropriate way.<br/>
|
||||
As a (very) trivial example, audio tracks can survive in most of the scenes of
|
||||
an application while meshes can be associated with a single scene and then
|
||||
discarded when users leave it.
|
||||
|
||||
A cache offers a set of basic functionalities to query its internal state and to
|
||||
_organize_ it:
|
||||
|
||||
```cpp
|
||||
// gets the number of resources managed by a cache
|
||||
const auto size = cache.size();
|
||||
|
||||
// checks if a cache contains at least a valid resource
|
||||
const auto empty = cache.empty();
|
||||
|
||||
// clears a cache and discards its content
|
||||
cache.clear();
|
||||
```
|
||||
|
||||
Besides these member functions, a cache contains what is needed to load, use and
|
||||
discard resources of the given type.<br/>
|
||||
Before exploring this part of the interface, it makes sense to mention how
|
||||
resources are identified. They have type `id_type` and therefore they can be
|
||||
created explicitly as in the following example:
|
||||
|
||||
```cpp
|
||||
constexpr auto identifier = "my/resource/identifier"_hs;
|
||||
// this is equivalent to the following
|
||||
constexpr entt::id_type hs = entt::hashed_string{"my/resource/identifier"};
|
||||
```
|
||||
|
||||
The class `hashed_string` is described in a dedicated section, so I won't go in
|
||||
details here.
|
||||
|
||||
Resources are loaded and thus stored in a cache through the `load` member
|
||||
function. It accepts the loader to use as a template parameter, the resource
|
||||
identifier and the parameters used to construct the resource as arguments:
|
||||
|
||||
```cpp
|
||||
// uses the identifier declared above
|
||||
cache.load<my_loader>(identifier, 0);
|
||||
|
||||
// uses a hashed string directly
|
||||
cache.load<my_loader>("another/identifier"_hs, 42);
|
||||
```
|
||||
|
||||
The function returns a handle to the resource, whether it already exists or is
|
||||
loaded. In case the loader returns an invalid pointer, the handle is invalid as
|
||||
well and therefore it can be easily used with an `if` statement:
|
||||
|
||||
```cpp
|
||||
if(entt::resource_handle handle = cache.load<my_loader>("another/identifier"_hs, 42); handle) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Before trying to load a resource, the `contains` member function can be used to
|
||||
know if a cache already contains a specific resource:
|
||||
|
||||
```cpp
|
||||
auto exists = cache.contains("my/identifier"_hs);
|
||||
```
|
||||
|
||||
There exists also a member function to use to force a reload of an already
|
||||
existing resource if needed:
|
||||
|
||||
```cpp
|
||||
auto handle = cache.reload<my_loader>("another/identifier"_hs, 42);
|
||||
```
|
||||
|
||||
As above, the function returns a handle to the resource that is invalid in case
|
||||
of errors. The `reload` member function is a kind of alias of the following
|
||||
snippet:
|
||||
|
||||
```cpp
|
||||
cache.discard(identifier);
|
||||
cache.load<my_loader>(identifier, 42);
|
||||
```
|
||||
|
||||
Where the `discard` member function is used to get rid of a resource if loaded.
|
||||
In case the cache doesn't contain a resource for the given identifier, `discard`
|
||||
does nothing and returns immediately.
|
||||
|
||||
So far, so good. Resources are finally loaded and stored within the cache.<br/>
|
||||
They are returned to users in the form of handles. To get one of them later on:
|
||||
|
||||
```cpp
|
||||
auto handle = cache.handle("my/identifier"_hs);
|
||||
```
|
||||
|
||||
The idea behind a handle is the same of the flyweight pattern. In other terms,
|
||||
resources aren't copied around. Instead, instances are shared between handles.
|
||||
Users of a resource own a handle that guarantees that a resource isn't destroyed
|
||||
until all the handles are destroyed, even if the resource itself is removed from
|
||||
the cache.<br/>
|
||||
Handles are tiny objects both movable and copyable. They return the contained
|
||||
resource as a (possibly const) reference on request:
|
||||
|
||||
* By means of the `get` member function:
|
||||
|
||||
```cpp
|
||||
auto &resource = handle.get();
|
||||
```
|
||||
|
||||
* Using the proper cast operator:
|
||||
|
||||
```cpp
|
||||
auto &resource = handle;
|
||||
```
|
||||
|
||||
* Through the dereference operator:
|
||||
|
||||
```cpp
|
||||
auto &resource = *handle;
|
||||
```
|
||||
|
||||
The resource can also be accessed directly using the arrow operator if required:
|
||||
|
||||
```cpp
|
||||
auto value = handle->value;
|
||||
```
|
||||
|
||||
To test if a handle is still valid, the cast operator to `bool` allows users to
|
||||
use it in a guard:
|
||||
|
||||
```cpp
|
||||
if(handle) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Finally, in case there is the need to load a resource and thus to get a handle
|
||||
without storing the resource itself in the cache, users can rely on the `temp`
|
||||
member function template.<br/>
|
||||
The declaration is similar to that of `load`, a (possibly invalid) handle for
|
||||
the resource is returned also in this case:
|
||||
|
||||
```cpp
|
||||
if(auto handle = cache.temp<my_loader>(42); handle) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Do not forget to test the handle for validity. Otherwise, getting a reference to
|
||||
the resource it points may result in undefined behavior.
|
||||
581
docs/md/signal.md
Normal file
581
docs/md/signal.md
Normal file
@@ -0,0 +1,581 @@
|
||||
# Crash Course: events, signals and everything in between
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Delegate](#delegate)
|
||||
* [Runtime arguments](#runtime-arguments)
|
||||
* [Lambda support](#lambda-support)
|
||||
* [Signals](#signals)
|
||||
* [Event dispatcher](#event-dispatcher)
|
||||
* [Event emitter](#event-emitter)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
Signals are usually a core part of games and software architectures in
|
||||
general.<br/>
|
||||
Roughly speaking, they help to decouple the various parts of a system while
|
||||
allowing them to communicate with each other somehow.
|
||||
|
||||
The so called _modern C++_ comes with a tool that can be useful in these terms,
|
||||
the `std::function`. As an example, it can be used to create delegates.<br/>
|
||||
However, there is no guarantee that an `std::function` does not perform
|
||||
allocations under the hood and this could be problematic sometimes. Furthermore,
|
||||
it solves a problem but may not adapt well to other requirements that may arise
|
||||
from time to time.
|
||||
|
||||
In case that the flexibility and power of an `std::function` isn't required or
|
||||
if the price to pay for them is too high,` EnTT` offers a complete set of
|
||||
lightweight classes to solve the same and many other problems.
|
||||
|
||||
# Delegate
|
||||
|
||||
A delegate can be used as a general purpose invoker with no memory overhead for
|
||||
free functions and members provided along with an instance on which to invoke
|
||||
them.<br/>
|
||||
It doesn't claim to be a drop-in replacement for an `std::function`, so don't
|
||||
expect to use it whenever an `std::function` fits well. That said, it's most
|
||||
likely even a better fit than an `std::function` in a lot of cases, so expect to
|
||||
use it quite a lot anyway.
|
||||
|
||||
The interface is trivial. It offers a default constructor to create empty
|
||||
delegates:
|
||||
|
||||
```cpp
|
||||
entt::delegate<int(int)> delegate{};
|
||||
```
|
||||
|
||||
All what is needed to create an instance is to specify the type of the function
|
||||
the delegate will _contain_, that is the signature of the free function or the
|
||||
member one wants to assign to it.
|
||||
|
||||
Attempting to use an empty delegate by invoking its function call operator
|
||||
results in undefined behavior or most likely a crash. Before to use a delegate,
|
||||
it must be initialized.<br/>
|
||||
There exists a bunch of overloads of the `connect` member function to do that.
|
||||
As an example of use:
|
||||
|
||||
```cpp
|
||||
int f(int i) { return i; }
|
||||
|
||||
struct my_struct {
|
||||
int f(const int &i) const { return i; }
|
||||
};
|
||||
|
||||
// bind a free function to the delegate
|
||||
delegate.connect<&f>();
|
||||
|
||||
// bind a member function to the delegate
|
||||
my_struct instance;
|
||||
delegate.connect<&my_struct::f>(instance);
|
||||
```
|
||||
|
||||
The delegate class accepts also data members, if needed. In this case, the
|
||||
function type of the delegate is such that the parameter list is empty and the
|
||||
value of the data member is at least convertible to the return type.
|
||||
|
||||
Free functions having type equivalent to `void(T &, args...)` are accepted as
|
||||
well. The first argument `T &` is considered a payload and the function will
|
||||
receive it back every time it's invoked. In other terms, this works just fine
|
||||
with the above definition:
|
||||
|
||||
```cpp
|
||||
void g(const char &c, int i) { /* ... */ }
|
||||
const char c = 'c';
|
||||
|
||||
delegate.connect<&g>(c);
|
||||
delegate(42);
|
||||
```
|
||||
|
||||
The function `g` will be invoked with a reference to `c` and `42`. However, the
|
||||
function type of the delegate is still `void(int)`. This is also the signature
|
||||
of its function call operator.
|
||||
|
||||
Another interesting aspect of the delegate class is that it accepts also
|
||||
functions with a list of parameters that is shorter than that of the function
|
||||
type used to specialize the delegate itself.<br/>
|
||||
The following code is therefore perfectly valid:
|
||||
|
||||
```cpp
|
||||
void g() { /* ... */ }
|
||||
delegate.connect<&g>();
|
||||
delegate(42);
|
||||
```
|
||||
|
||||
Where the function type of the delegate is `void(int)` as above. It goes without
|
||||
saying that the extra arguments are silently discarded internally.<br/>
|
||||
This is a nice-to-have feature in a lot of cases, as an example when the
|
||||
`delegate` class is used as a building block of a signal-slot system.
|
||||
|
||||
To create and initialize a delegate at once, there are a few specialized
|
||||
constructors. Because of the rules of the language, the listener is provided by
|
||||
means of the `entt::connect_arg` variable template:
|
||||
|
||||
```cpp
|
||||
entt::delegate<int(int)> func{entt::connect_arg<&f>};
|
||||
```
|
||||
|
||||
Aside `connect`, a `disconnect` counterpart isn't provided. Instead, there
|
||||
exists a `reset` member function to use to clear a delegate.<br/>
|
||||
To know if a delegate is empty, it can be used explicitly in every conditional
|
||||
statement:
|
||||
|
||||
```cpp
|
||||
if(delegate) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Finally, to invoke a delegate, the function call operator is the way to go as
|
||||
already shown in the examples above:
|
||||
|
||||
```cpp
|
||||
auto ret = delegate(42);
|
||||
```
|
||||
|
||||
In all cases, the listeners don't have to strictly follow the signature of the
|
||||
delegate. As long as a listener can be invoked with the given arguments to yield
|
||||
a result that is convertible to the given result type, everything works just
|
||||
fine.
|
||||
|
||||
As a side note, members of classes may or may not be associated with instances.
|
||||
If they are not, the first argument of the function type must be that of the
|
||||
class on which the members operate and an instance of this class must obviously
|
||||
be passed when invoking the delegate:
|
||||
|
||||
```cpp
|
||||
entt::delegate<void(my_struct &, int)> delegate;
|
||||
delegate.connect<&my_struct::f>();
|
||||
|
||||
my_struct instance;
|
||||
delegate(instance, 42);
|
||||
```
|
||||
|
||||
In this case, it's not possible to deduce the function type since the first
|
||||
argument doesn't necessarily have to be a reference (for example, it can be a
|
||||
pointer, as well as a const reference).<br/>
|
||||
Therefore, the function type must be declared explicitly for unbound members.
|
||||
|
||||
## Runtime arguments
|
||||
|
||||
The `delegate` class is meant to be used primarily with template arguments.
|
||||
However, as a consequence of its design, it can also offer minimal support for
|
||||
runtime arguments.<br/>
|
||||
When used in this modality, some feature aren't supported though. In particular:
|
||||
|
||||
* Curried functions aren't accepted.
|
||||
* Functions with an argument list that differs from that of the delegate aren't
|
||||
supported.
|
||||
* Return type and types of arguments **must** coincide with those of the
|
||||
delegate and _being at least convertible_ isn't enough anymore.
|
||||
|
||||
Moreover, for a given function type `Ret(Args...)`, the signature of the
|
||||
functions connected at runtime must necessarily be `Ret(const void *, Args...)`.
|
||||
|
||||
Runtime arguments can be passed both to the constructor of a delegate and to the
|
||||
`connect` member function. An optional parameter is also accepted in both cases.
|
||||
This argument is used to pass arbitrary user data back and forth as a
|
||||
`const void *` upon invocation.<br/>
|
||||
To connect a function to a delegate _in the hard way_:
|
||||
|
||||
```cpp
|
||||
int func(const void *ptr, int i) { return *static_cast<const int *>(ptr) * i; }
|
||||
const int value = 42;
|
||||
|
||||
// use the constructor ...
|
||||
entt::delegate delegate{&func, &value};
|
||||
|
||||
// ... or the connect member function
|
||||
delegate.connect(&func, &value);
|
||||
```
|
||||
|
||||
The type of the delegate is deduced from the function if possible. In this case,
|
||||
since the first argument is an implementation detail, the deduced function type
|
||||
is `int(int)`.<br/>
|
||||
Invoking a delegate built in this way follows the same rules as previously
|
||||
explained.
|
||||
|
||||
## Lambda support
|
||||
|
||||
In general, the `delegate` class doesn't fully support lambda functions in all
|
||||
their nuances. The reason is pretty simple: a `delegate` isn't a drop-in
|
||||
replacement for an `std::function`. Instead, it tries to overcome the problems
|
||||
with the latter.<br/>
|
||||
That being said, non-capturing lambda functions are supported, even though some
|
||||
feature aren't available in this case.
|
||||
|
||||
This is a logical consequence of the support for connecting functions at
|
||||
runtime. Therefore, lambda functions undergo the same rules and
|
||||
limitations.<br/>
|
||||
In fact, since non-capturing lambda functions decay to pointers to functions,
|
||||
they can be used with a `delegate` as if they were _normal functions_ with
|
||||
optional payload:
|
||||
|
||||
```cpp
|
||||
my_struct instance;
|
||||
|
||||
// use the constructor ...
|
||||
entt::delegate delegate{+[](const void *ptr, int value) {
|
||||
return static_cast<const my_struct *>(ptr)->f(value);
|
||||
}, &instance};
|
||||
|
||||
// ... or the connect member function
|
||||
delegate.connect([](const void *ptr, int value) {
|
||||
return static_cast<const my_struct *>(ptr)->f(value);
|
||||
}, &instance);
|
||||
```
|
||||
|
||||
As above, the first parameter (`const void *`) isn't part of the function type
|
||||
of the delegate and is used to dispatch arbitrary user data back and forth. In
|
||||
other terms, the function type of the delegate above is `int(int)`.
|
||||
|
||||
# Signals
|
||||
|
||||
Signal handlers work with references to classes, function pointers and pointers
|
||||
to members. Listeners can be any kind of objects and users are in charge of
|
||||
connecting and disconnecting them from a signal to avoid crashes due to
|
||||
different lifetimes. On the other side, performance shouldn't be affected that
|
||||
much by the presence of such a signal handler.<br/>
|
||||
Signals make use of delegates internally and therefore they undergo the same
|
||||
rules and offer similar functionalities. It may be a good idea to consult the
|
||||
documentation of the `delegate` class for further information.
|
||||
|
||||
A signal handler can be used as a private data member without exposing any
|
||||
_publish_ functionality to the clients of a class. The basic idea is to impose a
|
||||
clear separation between the signal itself and the `sink` class, that is a tool
|
||||
to be used to connect and disconnect listeners on the fly.
|
||||
|
||||
The API of a signal handler is straightforward. If a collector is supplied to
|
||||
the signal when something is published, all the values returned by the listeners
|
||||
can be literally _collected_ and used later by the caller. Otherwise, the class
|
||||
works just like a plain signal that emits events from time to time.<br/>
|
||||
To create instances of signal handlers it is sufficient to provide the type of
|
||||
function to which they refer:
|
||||
|
||||
```cpp
|
||||
entt::sigh<void(int, char)> signal;
|
||||
```
|
||||
|
||||
Signals offer all the basic functionalities required to know how many listeners
|
||||
they contain (`size`) or if they contain at least a listener (`empty`), as well
|
||||
as a function to use to swap handlers (`swap`).
|
||||
|
||||
Besides them, there are member functions to use both to connect and disconnect
|
||||
listeners in all their forms by means of a sink:
|
||||
|
||||
```cpp
|
||||
void foo(int, char) { /* ... */ }
|
||||
|
||||
struct listener {
|
||||
void bar(const int &, char) { /* ... */ }
|
||||
};
|
||||
|
||||
// ...
|
||||
|
||||
entt::sink sink{signal};
|
||||
listener instance;
|
||||
|
||||
sink.connect<&foo>();
|
||||
sink.connect<&listener::bar>(instance);
|
||||
|
||||
// ...
|
||||
|
||||
// disconnects a free function
|
||||
sink.disconnect<&foo>();
|
||||
|
||||
// disconnect a member function of an instance
|
||||
sink.disconnect<&listener::bar>(instance);
|
||||
|
||||
// disconnect all member functions of an instance, if any
|
||||
sink.disconnect(instance);
|
||||
|
||||
// discards all listeners at once
|
||||
sink.disconnect();
|
||||
```
|
||||
|
||||
As shown above, the listeners don't have to strictly follow the signature of the
|
||||
signal. As long as a listener can be invoked with the given arguments to yield a
|
||||
result that is convertible to the given return type, everything works just
|
||||
fine.<br/>
|
||||
It's also possible to connect a listener before other listeners already
|
||||
contained by the signal. The `before` function returns a `sink` object correctly
|
||||
initialized for the purpose that can be used to connect one or more listeners in
|
||||
order and in the desired position:
|
||||
|
||||
```cpp
|
||||
sink.before<&foo>().connect<&listener::bar>(instance);
|
||||
```
|
||||
|
||||
In all cases, the `connect` member function returns by default a `connection`
|
||||
object to be used as an alternative to break a connection by means of its
|
||||
`release` member function. A `scoped_connection` can also be created from a
|
||||
connection. In this case, the link is broken automatically as soon as the object
|
||||
goes out of scope.
|
||||
|
||||
Once listeners are attached (or even if there are no listeners at all), events
|
||||
and data in general can be published through a signal by means of the `publish`
|
||||
member function:
|
||||
|
||||
```cpp
|
||||
signal.publish(42, 'c');
|
||||
```
|
||||
|
||||
To collect data, the `collect` member function should be used instead. Below is
|
||||
a minimal example to show how to use it:
|
||||
|
||||
```cpp
|
||||
int f() { return 0; }
|
||||
int g() { return 1; }
|
||||
|
||||
// ...
|
||||
|
||||
entt::sigh<int()> signal;
|
||||
entt::sink sink{signal};
|
||||
|
||||
sink.connect<&f>();
|
||||
sink.connect<&g>();
|
||||
|
||||
std::vector<int> vec{};
|
||||
signal.collect([&vec](int value) { vec.push_back(value); });
|
||||
|
||||
assert(vec[0] == 0);
|
||||
assert(vec[1] == 1);
|
||||
```
|
||||
|
||||
A collector must expose a function operator that accepts as an argument a type
|
||||
to which the return type of the listeners can be converted. Moreover, it can
|
||||
optionally return a boolean value that is true to stop collecting data, false
|
||||
otherwise. This way one can avoid calling all the listeners in case it isn't
|
||||
necessary.<br/>
|
||||
Functors can also be used in place of a lambda. Since the collector is copied
|
||||
when invoking the `collect` member function, `std::ref` is the way to go in this
|
||||
case:
|
||||
|
||||
```cpp
|
||||
struct my_collector {
|
||||
std::vector<int> vec{};
|
||||
|
||||
bool operator()(int v) noexcept {
|
||||
vec.push_back(v);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// ...
|
||||
|
||||
my_collector collector;
|
||||
signal.collect(std::ref(collector));
|
||||
```
|
||||
|
||||
# Event dispatcher
|
||||
|
||||
The event dispatcher class is designed so as to be used in a loop. It allows
|
||||
users both to trigger immediate events or to queue events to be published all
|
||||
together once per tick.<br/>
|
||||
This class shares part of its API with the one of the signal handler, but it
|
||||
doesn't require that all the types of events are specified when declared:
|
||||
|
||||
```cpp
|
||||
// define a general purpose dispatcher
|
||||
entt::dispatcher dispatcher{};
|
||||
```
|
||||
|
||||
In order to register an instance of a class to a dispatcher, its type must
|
||||
expose one or more member functions the arguments of which are such that `E &`
|
||||
can be converted to them for each type of event `E`, no matter what the return
|
||||
value is.<br/>
|
||||
The name of the member function aimed to receive the event must be provided to
|
||||
the `connect` member function of the sink in charge for the specific event:
|
||||
|
||||
```cpp
|
||||
struct an_event { int value; };
|
||||
struct another_event {};
|
||||
|
||||
struct listener {
|
||||
void receive(const an_event &) { /* ... */ }
|
||||
void method(const another_event &) { /* ... */ }
|
||||
};
|
||||
|
||||
// ...
|
||||
|
||||
listener listener;
|
||||
dispatcher.sink<an_event>().connect<&listener::receive>(listener);
|
||||
dispatcher.sink<another_event>().connect<&listener::method>(listener);
|
||||
```
|
||||
|
||||
The `disconnect` member function follows the same pattern and can be used to
|
||||
remove one listener at a time or all of them at once:
|
||||
|
||||
```cpp
|
||||
dispatcher.sink<an_event>().disconnect<&listener::receive>(listener);
|
||||
dispatcher.sink<another_event>().disconnect(listener);
|
||||
```
|
||||
|
||||
The `trigger` member function serves the purpose of sending an immediate event
|
||||
to all the listeners registered so far. It offers a convenient approach that
|
||||
relieves users from having to create the event itself. Instead, it's enough to
|
||||
specify the type of event and provide all the parameters required to construct
|
||||
it.<br/>
|
||||
As an example:
|
||||
|
||||
```cpp
|
||||
dispatcher.trigger<an_event>(42);
|
||||
dispatcher.trigger<another_event>();
|
||||
```
|
||||
|
||||
Listeners are invoked immediately, order of execution isn't guaranteed. This
|
||||
method can be used to push around urgent messages like an _is terminating_
|
||||
notification on a mobile app.
|
||||
|
||||
On the other hand, the `enqueue` member function queues messages together and
|
||||
allows to maintain control over the moment they are sent to listeners. The
|
||||
signature of this method is more or less the same of `trigger`:
|
||||
|
||||
```cpp
|
||||
dispatcher.enqueue<an_event>(42);
|
||||
dispatcher.enqueue<another_event>();
|
||||
```
|
||||
|
||||
Events are stored aside until the `update` member function is invoked, then all
|
||||
the messages that are still pending are sent to the listeners at once:
|
||||
|
||||
```cpp
|
||||
// emits all the events of the given type at once
|
||||
dispatcher.update<an_event>();
|
||||
|
||||
// emits all the events queued so far at once
|
||||
dispatcher.update();
|
||||
```
|
||||
|
||||
This way users can embed the dispatcher in a loop and literally dispatch events
|
||||
once per tick to their systems.
|
||||
|
||||
# Event emitter
|
||||
|
||||
A general purpose event emitter thought mainly for those cases where it comes to
|
||||
working with asynchronous stuff.<br/>
|
||||
Originally designed to fit the requirements of
|
||||
[`uvw`](https://github.com/skypjack/uvw) (a wrapper for `libuv` written in
|
||||
modern C++), it was adapted later to be included in this library.
|
||||
|
||||
To create a custom emitter type, derived classes must inherit directly from the
|
||||
base class as:
|
||||
|
||||
```cpp
|
||||
struct my_emitter: emitter<my_emitter> {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
The full list of accepted types of events isn't required. Handlers are created
|
||||
internally on the fly and thus each type of event is accepted by default.
|
||||
|
||||
Whenever an event is published, an emitter provides the listeners with a
|
||||
reference to itself along with a reference to the event. Therefore listeners
|
||||
have an handy way to work with it without incurring in the need of capturing a
|
||||
reference to the emitter itself.<br/>
|
||||
In addition, an opaque object is returned each time a connection is established
|
||||
between an emitter and a listener, allowing the caller to disconnect them at a
|
||||
later time.<br/>
|
||||
The opaque object used to handle connections is both movable and copyable. On
|
||||
the other side, an event emitter is movable but not copyable by default.
|
||||
|
||||
To create new instances of an emitter, no arguments are required:
|
||||
|
||||
```cpp
|
||||
my_emitter emitter{};
|
||||
```
|
||||
|
||||
Listeners must be movable and callable objects (free functions, lambdas,
|
||||
functors, `std::function`s, whatever) whose function type is compatible with:
|
||||
|
||||
```cpp
|
||||
void(Event &, my_emitter &)
|
||||
```
|
||||
|
||||
Where `Event` is the type of event they want to listen.<br/>
|
||||
There are two ways to attach a listener to an event emitter that differ
|
||||
slightly from each other:
|
||||
|
||||
* To register a long-lived listener, use the `on` member function. It is meant
|
||||
to register a listener designed to be invoked more than once for the given
|
||||
event type.<br/>
|
||||
As an example:
|
||||
|
||||
```cpp
|
||||
auto conn = emitter.on<my_event>([](const my_event &event, my_emitter &emitter) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
The connection object can be freely discarded. Otherwise, it can be used later
|
||||
to disconnect the listener if required.
|
||||
|
||||
* To register a short-lived listener, use the `once` member function. It is
|
||||
meant to register a listener designed to be invoked only once for the given
|
||||
event type. The listener is automatically disconnected after the first
|
||||
invocation.<br/>
|
||||
As an example:
|
||||
|
||||
```cpp
|
||||
auto conn = emitter.once<my_event>([](const my_event &event, my_emitter &emitter) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
The connection object can be freely discarded. Otherwise, it can be used later
|
||||
to disconnect the listener if required.
|
||||
|
||||
In both cases, the connection object can be used with the `erase` member
|
||||
function:
|
||||
|
||||
```cpp
|
||||
emitter.erase(conn);
|
||||
```
|
||||
|
||||
There are also two member functions to use either to disconnect all the
|
||||
listeners for a given type of event or to clear the emitter:
|
||||
|
||||
```cpp
|
||||
// removes all the listener for the specific event
|
||||
emitter.clear<my_event>();
|
||||
|
||||
// removes all the listeners registered so far
|
||||
emitter.clear();
|
||||
```
|
||||
|
||||
To send an event to all the listeners that are interested in it, the `publish`
|
||||
member function offers a convenient approach that relieves users from having to
|
||||
create the event:
|
||||
|
||||
```cpp
|
||||
struct my_event { int i; };
|
||||
|
||||
// ...
|
||||
|
||||
emitter.publish<my_event>(42);
|
||||
```
|
||||
|
||||
Finally, the `empty` member function tests if there exists at least either a
|
||||
listener registered with the event emitter or to a given type of event:
|
||||
|
||||
```cpp
|
||||
bool empty;
|
||||
|
||||
// checks if there is any listener registered for the specific event
|
||||
empty = emitter.empty<my_event>();
|
||||
|
||||
// checks it there are listeners registered with the event emitter
|
||||
empty = emitter.empty();
|
||||
```
|
||||
|
||||
In general, the event emitter is a handy tool when the derived classes _wrap_
|
||||
asynchronous operations, because it introduces a _nice-to-have_ model based on
|
||||
events and listeners that kindly hides the complexity behind the scenes. However
|
||||
it is not limited to such uses.
|
||||
107
docs/md/unreal.md
Normal file
107
docs/md/unreal.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# EnTT and Unreal Engine
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Enable Cpp17](#enable-cpp17)
|
||||
* [EnTT as a third party module](#entt-as-a-third-party-module)
|
||||
* [Include EnTT](#include-entt)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
## Enable Cpp17
|
||||
|
||||
As of writing (Unreal Engine v4.25), the default C++ standard of Unreal Engine
|
||||
is C++14.<br/>
|
||||
On the other hand, note that `EnTT` requires C++17 to compile. To enable it, in
|
||||
the main module of the project there should be a `<Game Name>.Build.cs` file,
|
||||
the constructor of which must contain the following lines:
|
||||
|
||||
```cs
|
||||
PCHUsage = PCHUsageMode.NoSharedPCHs;
|
||||
PrivatePCHHeaderFile = "<PCH filename>.h";
|
||||
CppStandard = CppStandardVersion.Cpp17;
|
||||
```
|
||||
|
||||
Replace `<PCH filename>.h` with the name of the already existing PCH header
|
||||
file, if any.<br/>
|
||||
In case the project doesn't already contain a file of this type, it's possible
|
||||
to create one with the following content:
|
||||
|
||||
```cpp
|
||||
#pragma once
|
||||
#include "CoreMinimal.h"
|
||||
```
|
||||
|
||||
Remember to remove any old `PCHUsage = <...>` line that was previously there. At
|
||||
this point, C++17 support should be in place.<br/>
|
||||
Try to compile the project to ensure it works as expected before following
|
||||
further steps.
|
||||
|
||||
Note that updating a *project* to C++17 doesn't necessarily mean that the IDE in
|
||||
use will also start to recognize its syntax.<br/>
|
||||
If the plan is to use C++17 in the project too, check the specific instructions
|
||||
for the IDE in use.
|
||||
|
||||
## EnTT as a third party module
|
||||
|
||||
Once this point is reached, the `Source` directory should look like this:
|
||||
|
||||
```
|
||||
Source
|
||||
| MyGame.Target.cs
|
||||
| MyGameEditor.Target.cs
|
||||
|
|
||||
+---MyGame
|
||||
| | MyGame.Build.cs
|
||||
| | MyGame.h (PCH Header file)
|
||||
|
|
||||
\---ThirdParty
|
||||
\---EnTT
|
||||
| EnTT.Build.cs
|
||||
|
|
||||
\---entt (GitHub repository content inside)
|
||||
```
|
||||
|
||||
To make this happen, create the folder `ThirdParty` under `Source` if it doesn't
|
||||
exist already. Then, add an `EnTT` folder under `ThirdParty`.<br/>
|
||||
Within the latter, create a new file `EnTT.Build.cs` with the following content:
|
||||
|
||||
```cs
|
||||
using System.IO;
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class EnTT: ModuleRules {
|
||||
public EnTT(ReadOnlyTargetRules Target) : base(Target) {
|
||||
Type = ModuleType.External;
|
||||
PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "entt", "src", "entt"));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The last line indicates that the actual files will be found in the directory
|
||||
`EnTT/entt/src/entt`.<br/>
|
||||
Download the repository for `EnTT` and place it next to `EnTT.Build.cs` or
|
||||
update the path above accordingly.
|
||||
|
||||
Finally, open the `<Game Name>.Build.cs` file and add `EnTT` as a dependency at
|
||||
the end of the list:
|
||||
|
||||
```cs
|
||||
PublicDependencyModuleNames.AddRange(new[] {
|
||||
"Core", "CoreUObject", "Engine", "InputCore", [...], "EnTT"
|
||||
});
|
||||
```
|
||||
|
||||
Note that some IDEs might require a restart to start recognizing the new module
|
||||
for code-highlighting features and such.
|
||||
|
||||
## Include EnTT
|
||||
|
||||
In any source file of the project, add `#include "entt.hpp"` or any other path
|
||||
to the file from `EnTT` to use it.<br/>
|
||||
Try to create a registry as `entt::registry registry;` to make sure everything
|
||||
compiles fine.
|
||||
299
scripts/amalgamate.py
Normal file
299
scripts/amalgamate.py
Normal file
@@ -0,0 +1,299 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
# amalgamate.py - Amalgamate C source and header files.
|
||||
# Copyright (c) 2012, Erik Edlund <erik.edlund@32767.se>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# * Neither the name of Erik Edlund, nor the names of its contributors may
|
||||
# be used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
class Amalgamation(object):
|
||||
|
||||
# Prepends self.source_path to file_path if needed.
|
||||
def actual_path(self, file_path):
|
||||
if not os.path.isabs(file_path):
|
||||
file_path = os.path.join(self.source_path, file_path)
|
||||
return file_path
|
||||
|
||||
# Search included file_path in self.include_paths and
|
||||
# in source_dir if specified.
|
||||
def find_included_file(self, file_path, source_dir):
|
||||
search_dirs = self.include_paths[:]
|
||||
if source_dir:
|
||||
search_dirs.insert(0, source_dir)
|
||||
|
||||
for search_dir in search_dirs:
|
||||
search_path = os.path.join(search_dir, file_path)
|
||||
if os.path.isfile(self.actual_path(search_path)):
|
||||
return search_path
|
||||
return None
|
||||
|
||||
def __init__(self, args):
|
||||
with open(args.config, 'r') as f:
|
||||
config = json.loads(f.read())
|
||||
for key in config:
|
||||
setattr(self, key, config[key])
|
||||
|
||||
self.verbose = args.verbose == "yes"
|
||||
self.prologue = args.prologue
|
||||
self.source_path = args.source_path
|
||||
self.included_files = []
|
||||
|
||||
# Generate the amalgamation and write it to the target file.
|
||||
def generate(self):
|
||||
amalgamation = ""
|
||||
|
||||
if self.prologue:
|
||||
with open(self.prologue, 'r') as f:
|
||||
amalgamation += datetime.datetime.now().strftime(f.read())
|
||||
|
||||
if self.verbose:
|
||||
print("Config:")
|
||||
print(" target = {0}".format(self.target))
|
||||
print(" working_dir = {0}".format(os.getcwd()))
|
||||
print(" include_paths = {0}".format(self.include_paths))
|
||||
print("Creating amalgamation:")
|
||||
for file_path in self.sources:
|
||||
# Do not check the include paths while processing the source
|
||||
# list, all given source paths must be correct.
|
||||
# actual_path = self.actual_path(file_path)
|
||||
print(" - processing \"{0}\"".format(file_path))
|
||||
t = TranslationUnit(file_path, self, True)
|
||||
amalgamation += t.content
|
||||
|
||||
with open(self.target, 'w') as f:
|
||||
f.write(amalgamation)
|
||||
|
||||
print("...done!\n")
|
||||
if self.verbose:
|
||||
print("Files processed: {0}".format(self.sources))
|
||||
print("Files included: {0}".format(self.included_files))
|
||||
print("")
|
||||
|
||||
|
||||
def _is_within(match, matches):
|
||||
for m in matches:
|
||||
if match.start() > m.start() and \
|
||||
match.end() < m.end():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class TranslationUnit(object):
|
||||
# // C++ comment.
|
||||
cpp_comment_pattern = re.compile(r"//.*?\n")
|
||||
|
||||
# /* C comment. */
|
||||
c_comment_pattern = re.compile(r"/\*.*?\*/", re.S)
|
||||
|
||||
# "complex \"stri\\\ng\" value".
|
||||
string_pattern = re.compile("[^']" r'".*?(?<=[^\\])"', re.S)
|
||||
|
||||
# Handle simple include directives. Support for advanced
|
||||
# directives where macros and defines needs to expanded is
|
||||
# not a concern right now.
|
||||
include_pattern = re.compile(
|
||||
r'#\s*include\s+(<|")(?P<path>.*?)("|>)', re.S)
|
||||
|
||||
# #pragma once
|
||||
pragma_once_pattern = re.compile(r'#\s*pragma\s+once', re.S)
|
||||
|
||||
# Search for pattern in self.content, add the match to
|
||||
# contexts if found and update the index accordingly.
|
||||
def _search_content(self, index, pattern, contexts):
|
||||
match = pattern.search(self.content, index)
|
||||
if match:
|
||||
contexts.append(match)
|
||||
return match.end()
|
||||
return index + 2
|
||||
|
||||
# Return all the skippable contexts, i.e., comments and strings
|
||||
def _find_skippable_contexts(self):
|
||||
# Find contexts in the content in which a found include
|
||||
# directive should not be processed.
|
||||
skippable_contexts = []
|
||||
|
||||
# Walk through the content char by char, and try to grab
|
||||
# skippable contexts using regular expressions when found.
|
||||
i = 1
|
||||
content_len = len(self.content)
|
||||
while i < content_len:
|
||||
j = i - 1
|
||||
current = self.content[i]
|
||||
previous = self.content[j]
|
||||
|
||||
if current == '"':
|
||||
# String value.
|
||||
i = self._search_content(j, self.string_pattern,
|
||||
skippable_contexts)
|
||||
elif current == '*' and previous == '/':
|
||||
# C style comment.
|
||||
i = self._search_content(j, self.c_comment_pattern,
|
||||
skippable_contexts)
|
||||
elif current == '/' and previous == '/':
|
||||
# C++ style comment.
|
||||
i = self._search_content(j, self.cpp_comment_pattern,
|
||||
skippable_contexts)
|
||||
else:
|
||||
# Skip to the next char.
|
||||
i += 1
|
||||
|
||||
return skippable_contexts
|
||||
|
||||
# Returns True if the match is within list of other matches
|
||||
|
||||
# Removes pragma once from content
|
||||
def _process_pragma_once(self):
|
||||
content_len = len(self.content)
|
||||
if content_len < len("#include <x>"):
|
||||
return 0
|
||||
|
||||
# Find contexts in the content in which a found include
|
||||
# directive should not be processed.
|
||||
skippable_contexts = self._find_skippable_contexts()
|
||||
|
||||
pragmas = []
|
||||
pragma_once_match = self.pragma_once_pattern.search(self.content)
|
||||
while pragma_once_match:
|
||||
if not _is_within(pragma_once_match, skippable_contexts):
|
||||
pragmas.append(pragma_once_match)
|
||||
|
||||
pragma_once_match = self.pragma_once_pattern.search(self.content,
|
||||
pragma_once_match.end())
|
||||
|
||||
# Handle all collected pragma once directives.
|
||||
prev_end = 0
|
||||
tmp_content = ''
|
||||
for pragma_match in pragmas:
|
||||
tmp_content += self.content[prev_end:pragma_match.start()]
|
||||
prev_end = pragma_match.end()
|
||||
tmp_content += self.content[prev_end:]
|
||||
self.content = tmp_content
|
||||
|
||||
# Include all trivial #include directives into self.content.
|
||||
def _process_includes(self):
|
||||
content_len = len(self.content)
|
||||
if content_len < len("#include <x>"):
|
||||
return 0
|
||||
|
||||
# Find contexts in the content in which a found include
|
||||
# directive should not be processed.
|
||||
skippable_contexts = self._find_skippable_contexts()
|
||||
|
||||
# Search for include directives in the content, collect those
|
||||
# which should be included into the content.
|
||||
includes = []
|
||||
include_match = self.include_pattern.search(self.content)
|
||||
while include_match:
|
||||
if not _is_within(include_match, skippable_contexts):
|
||||
include_path = include_match.group("path")
|
||||
search_same_dir = include_match.group(1) == '"'
|
||||
found_included_path = self.amalgamation.find_included_file(
|
||||
include_path, self.file_dir if search_same_dir else None)
|
||||
if found_included_path:
|
||||
includes.append((include_match, found_included_path))
|
||||
|
||||
include_match = self.include_pattern.search(self.content,
|
||||
include_match.end())
|
||||
|
||||
# Handle all collected include directives.
|
||||
prev_end = 0
|
||||
tmp_content = ''
|
||||
for include in includes:
|
||||
include_match, found_included_path = include
|
||||
tmp_content += self.content[prev_end:include_match.start()]
|
||||
tmp_content += "// {0}\n".format(include_match.group(0))
|
||||
if found_included_path not in self.amalgamation.included_files:
|
||||
t = TranslationUnit(found_included_path, self.amalgamation, False)
|
||||
tmp_content += t.content
|
||||
prev_end = include_match.end()
|
||||
tmp_content += self.content[prev_end:]
|
||||
self.content = tmp_content
|
||||
|
||||
return len(includes)
|
||||
|
||||
# Make all content processing
|
||||
def _process(self):
|
||||
if not self.is_root:
|
||||
self._process_pragma_once()
|
||||
self._process_includes()
|
||||
|
||||
def __init__(self, file_path, amalgamation, is_root):
|
||||
self.file_path = file_path
|
||||
self.file_dir = os.path.dirname(file_path)
|
||||
self.amalgamation = amalgamation
|
||||
self.is_root = is_root
|
||||
|
||||
self.amalgamation.included_files.append(self.file_path)
|
||||
|
||||
actual_path = self.amalgamation.actual_path(file_path)
|
||||
if not os.path.isfile(actual_path):
|
||||
raise IOError("File not found: \"{0}\"".format(file_path))
|
||||
with open(actual_path, 'r') as f:
|
||||
self.content = f.read()
|
||||
self._process()
|
||||
|
||||
|
||||
def main():
|
||||
description = "Amalgamate C source and header files."
|
||||
usage = " ".join([
|
||||
"amalgamate.py",
|
||||
"[-v]",
|
||||
"-c path/to/config.json",
|
||||
"-s path/to/source/dir",
|
||||
"[-p path/to/prologue.(c|h)]"
|
||||
])
|
||||
argsparser = argparse.ArgumentParser(
|
||||
description=description, usage=usage)
|
||||
|
||||
argsparser.add_argument("-v", "--verbose", dest="verbose",
|
||||
choices=["yes", "no"], metavar="", help="be verbose")
|
||||
|
||||
argsparser.add_argument("-c", "--config", dest="config",
|
||||
required=True, metavar="", help="path to a JSON config file")
|
||||
|
||||
argsparser.add_argument("-s", "--source", dest="source_path",
|
||||
required=True, metavar="", help="source code path")
|
||||
|
||||
argsparser.add_argument("-p", "--prologue", dest="prologue",
|
||||
required=False, metavar="", help="path to a C prologue file")
|
||||
|
||||
amalgamation = Amalgamation(argsparser.parse_args())
|
||||
amalgamation.generate()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
8
scripts/config.json
Normal file
8
scripts/config.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"project": "entt",
|
||||
"target": "single_include/entt/entt.hpp",
|
||||
"sources": [
|
||||
"src/entt/entt.hpp"
|
||||
],
|
||||
"include_paths": ["src"]
|
||||
}
|
||||
60
scripts/update_homebrew.sh
Executable file
60
scripts/update_homebrew.sh
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/bin/sh
|
||||
|
||||
# only argument should be the version to upgrade to
|
||||
if [ $# != 1 ]
|
||||
then
|
||||
echo "Expected a version tag like v2.7.1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION="$1"
|
||||
URL="https://github.com/skypjack/entt/archive/$VERSION.tar.gz"
|
||||
FORMULA="entt.rb"
|
||||
|
||||
echo "Updating homebrew package to $VERSION"
|
||||
|
||||
echo "Cloning..."
|
||||
git clone https://github.com/skypjack/homebrew-entt.git
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
cd homebrew-entt
|
||||
|
||||
# download the repo at the version
|
||||
# exit with error messages if curl fails
|
||||
echo "Curling..."
|
||||
curl "$URL" --location --fail --silent --show-error --output archive.tar.gz
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# compute sha256 hash
|
||||
echo "Hashing..."
|
||||
HASH="$(openssl sha256 archive.tar.gz | cut -d " " -f 2)"
|
||||
|
||||
# delete the archive
|
||||
rm archive.tar.gz
|
||||
|
||||
echo "Sedding..."
|
||||
|
||||
# change the url in the formula file
|
||||
# the slashes in the URL must be escaped
|
||||
ESCAPED_URL="$(echo "$URL" | sed -e 's/[\/&]/\\&/g')"
|
||||
sed -i -e '/url/s/".*"/"'$ESCAPED_URL'"/' $FORMULA
|
||||
|
||||
# change the hash in the formula file
|
||||
sed -i -e '/sha256/s/".*"/"'$HASH'"/' $FORMULA
|
||||
|
||||
# delete temporary file created by sed
|
||||
rm -rf "$FORMULA-e"
|
||||
|
||||
# update remote repo
|
||||
echo "Gitting..."
|
||||
git add entt.rb
|
||||
git commit -m "Update to $VERSION"
|
||||
git push origin master
|
||||
|
||||
# out of homebrew-entt dir
|
||||
cd ..
|
||||
42264
single_include/entt/entt.hpp
Normal file
42264
single_include/entt/entt.hpp
Normal file
File diff suppressed because it is too large
Load Diff
62
src/entt/config/config.h
Normal file
62
src/entt/config/config.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef ENTT_CONFIG_CONFIG_H
|
||||
#define ENTT_CONFIG_CONFIG_H
|
||||
|
||||
|
||||
#ifndef ENTT_NOEXCEPT
|
||||
# define ENTT_NOEXCEPT noexcept
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef ENTT_USE_ATOMIC
|
||||
# define ENTT_MAYBE_ATOMIC(Type) Type
|
||||
#else
|
||||
# include <atomic>
|
||||
# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef ENTT_ID_TYPE
|
||||
# include <cstdint>
|
||||
# define ENTT_ID_TYPE std::uint32_t
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef ENTT_PAGE_SIZE
|
||||
static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two");
|
||||
#else
|
||||
# define ENTT_PAGE_SIZE 4096
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef ENTT_DISABLE_ASSERT
|
||||
# undef ENTT_ASSERT
|
||||
# define ENTT_ASSERT(...) (void(0))
|
||||
#elif !defined ENTT_ASSERT
|
||||
# include <cassert>
|
||||
# define ENTT_ASSERT(condition) assert(condition)
|
||||
#endif
|
||||
|
||||
|
||||
#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__
|
||||
# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
|
||||
# define ENTT_PRETTY_FUNCTION_PREFIX '='
|
||||
# define ENTT_PRETTY_FUNCTION_SUFFIX ']'
|
||||
# elif defined _MSC_VER
|
||||
# define ENTT_PRETTY_FUNCTION __FUNCSIG__
|
||||
# define ENTT_PRETTY_FUNCTION_PREFIX '<'
|
||||
# define ENTT_PRETTY_FUNCTION_SUFFIX '>'
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
10
src/entt/config/version.h
Normal file
10
src/entt/config/version.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef ENTT_CONFIG_VERSION_H
|
||||
#define ENTT_CONFIG_VERSION_H
|
||||
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 7
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
|
||||
|
||||
#endif
|
||||
144
src/entt/core/algorithm.hpp
Normal file
144
src/entt/core/algorithm.hpp
Normal file
@@ -0,0 +1,144 @@
|
||||
#ifndef ENTT_CORE_ALGORITHM_HPP
|
||||
#define ENTT_CORE_ALGORITHM_HPP
|
||||
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include "utility.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Function object to wrap `std::sort` in a class type.
|
||||
*
|
||||
* Unfortunately, `std::sort` cannot be passed as template argument to a class
|
||||
* template or a function template.<br/>
|
||||
* This class fills the gap by wrapping some flavors of `std::sort` in a
|
||||
* function object.
|
||||
*/
|
||||
struct std_sort {
|
||||
/**
|
||||
* @brief Sorts the elements in a range.
|
||||
*
|
||||
* Sorts the elements in a range using the given binary comparison function.
|
||||
*
|
||||
* @tparam It Type of random access iterator.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function.
|
||||
* @param first An iterator to the first element of the range to sort.
|
||||
* @param last An iterator past the last element of the range to sort.
|
||||
* @param compare A valid comparison function object.
|
||||
* @param args Arguments to forward to the sort function, if any.
|
||||
*/
|
||||
template<typename It, typename Compare = std::less<>, typename... Args>
|
||||
void operator()(It first, It last, Compare compare = Compare{}, Args &&... args) const {
|
||||
std::sort(std::forward<Args>(args)..., std::move(first), std::move(last), std::move(compare));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*! @brief Function object for performing insertion sort. */
|
||||
struct insertion_sort {
|
||||
/**
|
||||
* @brief Sorts the elements in a range.
|
||||
*
|
||||
* Sorts the elements in a range using the given binary comparison function.
|
||||
*
|
||||
* @tparam It Type of random access iterator.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @param first An iterator to the first element of the range to sort.
|
||||
* @param last An iterator past the last element of the range to sort.
|
||||
* @param compare A valid comparison function object.
|
||||
*/
|
||||
template<typename It, typename Compare = std::less<>>
|
||||
void operator()(It first, It last, Compare compare = Compare{}) const {
|
||||
if(first < last) {
|
||||
for(auto it = first + 1; it < last; ++it) {
|
||||
auto value = std::move(*it);
|
||||
auto pre = it;
|
||||
|
||||
for(; pre > first && compare(value, *(pre-1)); --pre) {
|
||||
*pre = std::move(*(pre-1));
|
||||
}
|
||||
|
||||
*pre = std::move(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Function object for performing LSD radix sort.
|
||||
* @tparam Bit Number of bits processed per pass.
|
||||
* @tparam N Maximum number of bits to sort.
|
||||
*/
|
||||
template<std::size_t Bit, std::size_t N>
|
||||
struct radix_sort {
|
||||
static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass");
|
||||
|
||||
/**
|
||||
* @brief Sorts the elements in a range.
|
||||
*
|
||||
* Sorts the elements in a range using the given _getter_ to access the
|
||||
* actual data to be sorted.
|
||||
*
|
||||
* This implementation is inspired by the online book
|
||||
* [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort).
|
||||
*
|
||||
* @tparam It Type of random access iterator.
|
||||
* @tparam Getter Type of _getter_ function object.
|
||||
* @param first An iterator to the first element of the range to sort.
|
||||
* @param last An iterator past the last element of the range to sort.
|
||||
* @param getter A valid _getter_ function object.
|
||||
*/
|
||||
template<typename It, typename Getter = identity>
|
||||
void operator()(It first, It last, Getter getter = Getter{}) const {
|
||||
if(first < last) {
|
||||
static constexpr auto mask = (1 << Bit) - 1;
|
||||
static constexpr auto buckets = 1 << Bit;
|
||||
static constexpr auto passes = N / Bit;
|
||||
|
||||
using value_type = typename std::iterator_traits<It>::value_type;
|
||||
std::vector<value_type> aux(std::distance(first, last));
|
||||
|
||||
auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) {
|
||||
std::size_t index[buckets]{};
|
||||
std::size_t count[buckets]{};
|
||||
|
||||
for(auto it = from; it != to; ++it) {
|
||||
++count[(getter(*it) >> start) & mask];
|
||||
}
|
||||
|
||||
for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) {
|
||||
index[pos + 1u] = index[pos] + count[pos];
|
||||
}
|
||||
|
||||
for(auto it = from; it != to; ++it) {
|
||||
out[index[(getter(*it) >> start) & mask]++] = std::move(*it);
|
||||
}
|
||||
};
|
||||
|
||||
for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) {
|
||||
part(first, last, aux.begin(), pass * Bit);
|
||||
part(aux.begin(), aux.end(), first, (pass + 1) * Bit);
|
||||
}
|
||||
|
||||
if constexpr(passes & 1) {
|
||||
part(first, last, aux.begin(), (passes - 1) * Bit);
|
||||
std::move(aux.begin(), aux.end(), first);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
407
src/entt/core/any.hpp
Normal file
407
src/entt/core/any.hpp
Normal file
@@ -0,0 +1,407 @@
|
||||
#ifndef ENTT_CORE_ANY_HPP
|
||||
#define ENTT_CORE_ANY_HPP
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
#include "type_info.hpp"
|
||||
#include "type_traits.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief A SBO friendly, type-safe container for single values of any type.
|
||||
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||
* @tparam Align Optional alignment requirement.
|
||||
*/
|
||||
template<std::size_t Len, std::size_t Align>
|
||||
class basic_any {
|
||||
enum class operation { COPY, MOVE, DTOR, COMP, ADDR, CADDR, REF, CREF, TYPE };
|
||||
|
||||
using storage_type = std::aligned_storage_t<Len + !Len, Align>;
|
||||
using vtable_type = const void *(const operation, const basic_any &, const void *);
|
||||
|
||||
template<typename Type>
|
||||
static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>;
|
||||
|
||||
template<typename Type>
|
||||
[[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<typename Type>
|
||||
static Type & as(const void *to) {
|
||||
return *const_cast<Type *>(static_cast<const Type *>(to));
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
static const void * basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &from, [[maybe_unused]] const void *to) {
|
||||
if constexpr(!std::is_void_v<Type>) {
|
||||
if constexpr(std::is_lvalue_reference_v<Type>) {
|
||||
using base_type = std::remove_const_t<std::remove_reference_t<Type>>;
|
||||
|
||||
switch(op) {
|
||||
case operation::COPY:
|
||||
if constexpr(std::is_copy_constructible_v<base_type>) {
|
||||
as<basic_any>(to).template emplace<base_type>(*static_cast<const base_type *>(from.instance));
|
||||
}
|
||||
break;
|
||||
case operation::MOVE:
|
||||
as<basic_any>(to).instance = from.instance;
|
||||
[[fallthrough]];
|
||||
case operation::DTOR:
|
||||
break;
|
||||
case operation::COMP:
|
||||
return compare<base_type>(from.instance, to) ? to : nullptr;
|
||||
case operation::ADDR:
|
||||
return std::is_const_v<std::remove_reference_t<Type>> ? nullptr : from.instance;
|
||||
case operation::CADDR:
|
||||
return from.instance;
|
||||
case operation::REF:
|
||||
as<basic_any>(to).instance = from.instance;
|
||||
as<basic_any>(to).vtable = basic_vtable<Type>;
|
||||
break;
|
||||
case operation::CREF:
|
||||
as<basic_any>(to).instance = from.instance;
|
||||
as<basic_any>(to).vtable = basic_vtable<const base_type &>;
|
||||
break;
|
||||
case operation::TYPE:
|
||||
as<type_info>(to) = type_id<base_type>();
|
||||
break;
|
||||
}
|
||||
} else if constexpr(in_situ<Type>) {
|
||||
#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L
|
||||
auto *instance = const_cast<Type *>(std::launder(reinterpret_cast<const Type *>(&from.storage)));
|
||||
#else
|
||||
auto *instance = const_cast<Type *>(reinterpret_cast<const Type *>(&from.storage));
|
||||
#endif
|
||||
|
||||
switch(op) {
|
||||
case operation::COPY:
|
||||
if constexpr(std::is_copy_constructible_v<Type>) {
|
||||
new (&as<basic_any>(to).storage) Type{std::as_const(*instance)};
|
||||
as<basic_any>(to).vtable = from.vtable;
|
||||
}
|
||||
break;
|
||||
case operation::MOVE:
|
||||
new (&as<basic_any>(to).storage) Type{std::move(*instance)};
|
||||
[[fallthrough]];
|
||||
case operation::DTOR:
|
||||
instance->~Type();
|
||||
break;
|
||||
case operation::COMP:
|
||||
return compare<Type>(instance, to) ? to : nullptr;
|
||||
case operation::ADDR:
|
||||
case operation::CADDR:
|
||||
return instance;
|
||||
case operation::REF:
|
||||
as<basic_any>(to).instance = instance;
|
||||
as<basic_any>(to).vtable = basic_vtable<Type &>;
|
||||
break;
|
||||
case operation::CREF:
|
||||
as<basic_any>(to).instance = instance;
|
||||
as<basic_any>(to).vtable = basic_vtable<const Type &>;
|
||||
break;
|
||||
case operation::TYPE:
|
||||
as<type_info>(to) = type_id<Type>();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch(op) {
|
||||
case operation::COPY:
|
||||
if constexpr(std::is_copy_constructible_v<Type>) {
|
||||
as<basic_any>(to).instance = new Type{*static_cast<const Type *>(from.instance)};
|
||||
as<basic_any>(to).vtable = from.vtable;
|
||||
}
|
||||
break;
|
||||
case operation::MOVE:
|
||||
as<basic_any>(to).instance = from.instance;
|
||||
break;
|
||||
case operation::DTOR:
|
||||
if constexpr(std::is_array_v<Type>) {
|
||||
delete[] static_cast<const Type *>(from.instance);
|
||||
} else {
|
||||
delete static_cast<const Type *>(from.instance);
|
||||
}
|
||||
break;
|
||||
case operation::COMP:
|
||||
return compare<Type>(from.instance, to) ? to : nullptr;
|
||||
case operation::ADDR:
|
||||
case operation::CADDR:
|
||||
return from.instance;
|
||||
case operation::REF:
|
||||
as<basic_any>(to).instance = from.instance;
|
||||
as<basic_any>(to).vtable = basic_vtable<Type &>;
|
||||
break;
|
||||
case operation::CREF:
|
||||
as<basic_any>(to).instance = from.instance;
|
||||
as<basic_any>(to).vtable = basic_vtable<const Type &>;
|
||||
break;
|
||||
case operation::TYPE:
|
||||
as<type_info>(to) = type_id<Type>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Default constructor. */
|
||||
basic_any() ENTT_NOEXCEPT
|
||||
: basic_any{std::in_place_type<void>}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Constructs an any by directly initializing the new object.
|
||||
* @tparam Type Type of object to use to initialize the wrapper.
|
||||
* @tparam Args Types of arguments to use to construct the new instance.
|
||||
* @param args Parameters to use to construct the instance.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
explicit basic_any(std::in_place_type_t<Type>, [[maybe_unused]] Args &&... args)
|
||||
: instance{},
|
||||
vtable{&basic_vtable<Type>}
|
||||
{
|
||||
if constexpr(!std::is_void_v<Type>) {
|
||||
if constexpr(std::is_lvalue_reference_v<Type>) {
|
||||
static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
|
||||
instance = (std::addressof(args), ...);
|
||||
} else if constexpr(in_situ<Type>) {
|
||||
new (&storage) Type(std::forward<Args>(args)...);
|
||||
} else {
|
||||
instance = new Type(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs an any that holds an unmanaged object.
|
||||
* @tparam Type Type of object to use to initialize the wrapper.
|
||||
* @param value An instance of an object to use to initialize the wrapper.
|
||||
*/
|
||||
template<typename Type>
|
||||
basic_any(std::reference_wrapper<Type> value) ENTT_NOEXCEPT
|
||||
: basic_any{std::in_place_type<Type &>, value.get()}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Constructs an any from a given value.
|
||||
* @tparam Type Type of object to use to initialize the wrapper.
|
||||
* @param value An instance of an object to use to initialize the wrapper.
|
||||
*/
|
||||
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
|
||||
basic_any(Type &&value)
|
||||
: basic_any{std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
*/
|
||||
basic_any(const basic_any &other)
|
||||
: basic_any{}
|
||||
{
|
||||
other.vtable(operation::COPY, other, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_any(basic_any &&other) ENTT_NOEXCEPT
|
||||
: basic_any{}
|
||||
{
|
||||
other.vtable(operation::MOVE, other, this);
|
||||
vtable = std::exchange(other.vtable, &basic_vtable<void>);
|
||||
}
|
||||
|
||||
/*! @brief Frees the internal storage, whatever it means. */
|
||||
~basic_any() {
|
||||
vtable(operation::DTOR, *this, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assignment operator.
|
||||
* @param other The instance to assign from.
|
||||
* @return This any object.
|
||||
*/
|
||||
basic_any & operator=(basic_any other) {
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the type of the contained object.
|
||||
* @return The type of the contained object, if any.
|
||||
*/
|
||||
[[nodiscard]] type_info type() const ENTT_NOEXCEPT {
|
||||
type_info info;
|
||||
vtable(operation::TYPE, *this, &info);
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an opaque pointer to the contained instance.
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
[[nodiscard]] const void * data() const ENTT_NOEXCEPT {
|
||||
return vtable(operation::CADDR, *this, nullptr);
|
||||
}
|
||||
|
||||
/*! @copydoc data */
|
||||
[[nodiscard]] void * data() ENTT_NOEXCEPT {
|
||||
return const_cast<void *>(vtable(operation::ADDR, *this, nullptr));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Replaces the contained object by creating a new instance directly.
|
||||
* @tparam Type Type of object to use to initialize the wrapper.
|
||||
* @tparam Args Types of arguments to use to construct the new instance.
|
||||
* @param args Parameters to use to construct the instance.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
void emplace(Args &&... args) {
|
||||
*this = basic_any{std::in_place_type<Type>, std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
/*! @brief Destroys contained object */
|
||||
void reset() {
|
||||
*this = basic_any{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns false if a wrapper is empty, true otherwise.
|
||||
* @return False if the wrapper is empty, true otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return !(vtable(operation::CADDR, *this, nullptr) == nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if two wrappers differ in their content.
|
||||
* @param other Wrapper with which to compare.
|
||||
* @return False if the two objects differ in their content, true otherwise.
|
||||
*/
|
||||
bool operator==(const basic_any &other) const ENTT_NOEXCEPT {
|
||||
return type() == other.type() && (vtable(operation::COMP, *this, other.data()) == other.data());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swaps two any objects.
|
||||
* @param lhs A valid any object.
|
||||
* @param rhs A valid any object.
|
||||
*/
|
||||
friend void swap(basic_any &lhs, basic_any &rhs) {
|
||||
basic_any tmp{};
|
||||
lhs.vtable(operation::MOVE, lhs, &tmp);
|
||||
rhs.vtable(operation::MOVE, rhs, &lhs);
|
||||
lhs.vtable(operation::MOVE, tmp, &rhs);
|
||||
std::swap(lhs.vtable, rhs.vtable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Aliasing constructor.
|
||||
* @return An any that shares a reference to an unmanaged object.
|
||||
*/
|
||||
[[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT {
|
||||
basic_any ref{};
|
||||
vtable(operation::REF, *this, &ref);
|
||||
return ref;
|
||||
}
|
||||
|
||||
/*! @copydoc as_ref */
|
||||
[[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT {
|
||||
basic_any ref{};
|
||||
vtable(operation::CREF, *this, &ref);
|
||||
return ref;
|
||||
}
|
||||
|
||||
private:
|
||||
union { const void *instance; storage_type storage; };
|
||||
vtable_type *vtable;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Checks if two wrappers differ in their content.
|
||||
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||
* @tparam Align Alignment requirement.
|
||||
* @param lhs A wrapper, either empty or not.
|
||||
* @param rhs A wrapper, either empty or not.
|
||||
* @return True if the two wrappers differ in their content, false otherwise.
|
||||
*/
|
||||
template<std::size_t Len, std::size_t Align>
|
||||
[[nodiscard]] inline bool operator!=(const basic_any<Len, Align> &lhs, const basic_any<Len, Align> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Performs type-safe access to the contained object.
|
||||
* @tparam Type Type to which conversion is required.
|
||||
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||
* @tparam Align Alignment requirement.
|
||||
* @param data Target any object.
|
||||
* @return The element converted to the requested type.
|
||||
*/
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT {
|
||||
const auto * const instance = any_cast<std::remove_reference_t<Type>>(&data);
|
||||
ENTT_ASSERT(instance);
|
||||
return static_cast<Type>(*instance);
|
||||
}
|
||||
|
||||
|
||||
/*! @copydoc any_cast */
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
Type any_cast(basic_any<Len, Align> &data) ENTT_NOEXCEPT {
|
||||
// forces const on non-reference types to make them work also with wrappers for const references
|
||||
auto * const instance = any_cast<std::conditional_t<std::is_reference_v<Type>, std::remove_reference_t<Type>, const Type>>(&data);
|
||||
ENTT_ASSERT(instance);
|
||||
return static_cast<Type>(*instance);
|
||||
}
|
||||
|
||||
|
||||
/*! @copydoc any_cast */
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
Type any_cast(basic_any<Len, Align> &&data) ENTT_NOEXCEPT {
|
||||
// forces const on non-reference types to make them work also with wrappers for const references
|
||||
auto * const instance = any_cast<std::conditional_t<std::is_reference_v<Type>, std::remove_reference_t<Type>, const Type>>(&data);
|
||||
ENTT_ASSERT(instance);
|
||||
return static_cast<Type>(std::move(*instance));
|
||||
}
|
||||
|
||||
|
||||
/*! @copydoc any_cast */
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
const Type * any_cast(const basic_any<Len, Align> *data) ENTT_NOEXCEPT {
|
||||
return (data->type() == type_id<Type>() ? static_cast<const Type *>(data->data()) : nullptr);
|
||||
}
|
||||
|
||||
|
||||
/*! @copydoc any_cast */
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
Type * any_cast(basic_any<Len, Align> *data) ENTT_NOEXCEPT {
|
||||
// last attempt to make wrappers for const references return their values
|
||||
return (data->type() == type_id<Type>() ? static_cast<Type *>(static_cast<constness_as_t<basic_any<Len, Align>, Type> *>(data)->data()) : nullptr);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#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<cstddef>
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
@@ -17,34 +17,21 @@ namespace entt {
|
||||
* identifiers.
|
||||
*/
|
||||
template<typename...>
|
||||
class Family {
|
||||
static std::size_t identifier() noexcept {
|
||||
static std::size_t value = 0;
|
||||
return value++;
|
||||
}
|
||||
|
||||
template<typename...>
|
||||
static std::size_t family() noexcept {
|
||||
static const std::size_t value = identifier();
|
||||
return value;
|
||||
}
|
||||
class family {
|
||||
inline static ENTT_MAYBE_ATOMIC(id_type) identifier{};
|
||||
|
||||
public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using family_type = std::size_t;
|
||||
using family_type = id_type;
|
||||
|
||||
/**
|
||||
* @brief Returns an unique identifier for the given type.
|
||||
* @return Statically generated unique identifier for the given type.
|
||||
*/
|
||||
/*! @brief Statically generated unique identifier for the given type. */
|
||||
template<typename... Type>
|
||||
static family_type type() noexcept {
|
||||
return family<std::decay_t<Type>...>();
|
||||
}
|
||||
// at the time I'm writing, clang crashes during compilation if auto is used instead of family_type
|
||||
inline static const family_type type = identifier++;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_CORE_FAMILY_HPP
|
||||
#endif
|
||||
|
||||
27
src/entt/core/fwd.hpp
Normal file
27
src/entt/core/fwd.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef ENTT_CORE_FWD_HPP
|
||||
#define ENTT_CORE_FWD_HPP
|
||||
|
||||
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
template<std::size_t Len, std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
|
||||
class basic_any;
|
||||
|
||||
|
||||
/*! @brief Alias declaration for type identifiers. */
|
||||
using id_type = ENTT_ID_TYPE;
|
||||
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using any = basic_any<sizeof(double[2])>;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -4,107 +4,266 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Zero overhead resource identifier.
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
|
||||
namespace internal {
|
||||
|
||||
|
||||
template<typename>
|
||||
struct fnv1a_traits;
|
||||
|
||||
|
||||
template<>
|
||||
struct fnv1a_traits<std::uint32_t> {
|
||||
using type = std::uint32_t;
|
||||
static constexpr std::uint32_t offset = 2166136261;
|
||||
static constexpr std::uint32_t prime = 16777619;
|
||||
};
|
||||
|
||||
|
||||
template<>
|
||||
struct fnv1a_traits<std::uint64_t> {
|
||||
using type = std::uint64_t;
|
||||
static constexpr std::uint64_t offset = 14695981039346656037ull;
|
||||
static constexpr std::uint64_t prime = 1099511628211ull;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Zero overhead unique identifier.
|
||||
*
|
||||
* A hashed string is a compile-time tool that allows users to use
|
||||
* human-readable identifers in the codebase while using their numeric
|
||||
* counterparts at runtime.<br/>
|
||||
* Because of that, a hashed string can also be used in constant expressions if
|
||||
* required.
|
||||
*
|
||||
* @tparam Char Character type.
|
||||
*/
|
||||
class HashedString final {
|
||||
struct ConstCharWrapper final {
|
||||
template<typename Char>
|
||||
class basic_hashed_string {
|
||||
using traits_type = internal::fnv1a_traits<id_type>;
|
||||
|
||||
struct const_wrapper {
|
||||
// non-explicit constructor on purpose
|
||||
constexpr ConstCharWrapper(const char *str) noexcept: str{str} {}
|
||||
const char *str;
|
||||
constexpr const_wrapper(const Char *curr) ENTT_NOEXCEPT: str{curr} {}
|
||||
const Char *str;
|
||||
};
|
||||
|
||||
static constexpr std::uint64_t offset = 14695981039346656037ull;
|
||||
static constexpr std::uint64_t prime = 1099511628211ull;
|
||||
|
||||
// Fowler–Noll–Vo hash function v. 1a - the good
|
||||
static constexpr std::uint64_t helper(std::uint64_t partial, const char *str) noexcept {
|
||||
return str[0] == 0 ? partial : helper((partial^str[0])*prime, str+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 = std::uint64_t;
|
||||
using hash_type = id_type;
|
||||
|
||||
/**
|
||||
* @brief Constructs a hashed string from an array of const chars.
|
||||
* @brief Returns directly the numeric representation of a string view.
|
||||
* @param str Human-readable identifer.
|
||||
* @param size Length of the string to hash.
|
||||
* @return The numeric representation of the string.
|
||||
*/
|
||||
[[nodiscard]] static constexpr hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT {
|
||||
id_type partial{traits_type::offset};
|
||||
while(size--) { partial = (partial^(str++)[0])*traits_type::prime; }
|
||||
return partial;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns directly the numeric representation of a string.
|
||||
*
|
||||
* Forcing template resolution avoids implicit conversions. An
|
||||
* human-readable identifier can be anything but a plain, old bunch of
|
||||
* characters.<br/>
|
||||
* Example of use:
|
||||
* @code{.cpp}
|
||||
* HashedString sh{"my.png"};
|
||||
* const auto value = basic_hashed_string<char>::to_value("my.png");
|
||||
* @endcode
|
||||
*
|
||||
* @tparam N Number of characters of the identifier.
|
||||
* @param str Human-readable identifer.
|
||||
* @return The numeric representation of the string.
|
||||
*/
|
||||
template <std::size_t N>
|
||||
constexpr HashedString(const char (&str)[N]) noexcept
|
||||
: hash{helper(offset, str)}, str{str}
|
||||
template<std::size_t N>
|
||||
[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT {
|
||||
return helper(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns directly the numeric representation of a string.
|
||||
* @param wrapper Helps achieving the purpose by relying on overloading.
|
||||
* @return The numeric representation of the string.
|
||||
*/
|
||||
[[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT {
|
||||
return helper(wrapper.str);
|
||||
}
|
||||
|
||||
/*! @brief Constructs an empty hashed string. */
|
||||
constexpr basic_hashed_string() ENTT_NOEXCEPT
|
||||
: str{nullptr}, hash{}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Constructs a hashed string from an array of const characters.
|
||||
*
|
||||
* Forcing template resolution avoids implicit conversions. An
|
||||
* human-readable identifier can be anything but a plain, old bunch of
|
||||
* characters.<br/>
|
||||
* Example of use:
|
||||
* @code{.cpp}
|
||||
* basic_hashed_string<char> hs{"my.png"};
|
||||
* @endcode
|
||||
*
|
||||
* @tparam N Number of characters of the identifier.
|
||||
* @param curr Human-readable identifer.
|
||||
*/
|
||||
template<std::size_t N>
|
||||
constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT
|
||||
: str{curr}, hash{helper(curr)}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Explicit constructor on purpose to avoid constructing a hashed
|
||||
* string directly from a `const char *`.
|
||||
*
|
||||
* string directly from a `const value_type *`.
|
||||
* @param wrapper Helps achieving the purpose by relying on overloading.
|
||||
*/
|
||||
explicit constexpr HashedString(ConstCharWrapper wrapper) noexcept
|
||||
: hash{helper(offset, wrapper.str)}, str{wrapper.str}
|
||||
explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT
|
||||
: str{wrapper.str}, hash{helper(wrapper.str)}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Returns the human-readable representation of a hashed string.
|
||||
* @return The string used to initialize the instance.
|
||||
*/
|
||||
constexpr operator const char *() const noexcept { return str; }
|
||||
[[nodiscard]] constexpr const value_type * data() const ENTT_NOEXCEPT {
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the numeric representation of a hashed string.
|
||||
* @return The numeric representation of the instance.
|
||||
*/
|
||||
constexpr operator hash_type() const noexcept { return hash; }
|
||||
[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT {
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*! @copydoc data */
|
||||
[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { return data(); }
|
||||
|
||||
/**
|
||||
* @brief Returns the numeric representation of a hashed string.
|
||||
* @return The numeric representation of the instance.
|
||||
*/
|
||||
[[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); }
|
||||
|
||||
/**
|
||||
* @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 HashedString &other) const noexcept {
|
||||
[[nodiscard]] constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT {
|
||||
return hash == other.hash;
|
||||
}
|
||||
|
||||
private:
|
||||
const hash_type hash;
|
||||
const char *str;
|
||||
const value_type *str;
|
||||
hash_type hash;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
*
|
||||
* It allows to deduce the character type of the hashed string directly from a
|
||||
* human-readable identifer provided to the constructor.
|
||||
*
|
||||
* @tparam Char Character type.
|
||||
* @tparam N Number of characters of the identifier.
|
||||
* @param str Human-readable identifer.
|
||||
*/
|
||||
template<typename Char, std::size_t N>
|
||||
basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT
|
||||
-> basic_hashed_string<Char>;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Compares two hashed strings.
|
||||
* @tparam Char Character type.
|
||||
* @param lhs A valid hashed string.
|
||||
* @param rhs A valid hashed string.
|
||||
* @return True if the two hashed strings are identical, false otherwise.
|
||||
*/
|
||||
constexpr bool operator!=(const HashedString &lhs, const HashedString &rhs) noexcept {
|
||||
template<typename Char>
|
||||
[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
|
||||
/*! @brief Aliases for common character types. */
|
||||
using hashed_string = basic_hashed_string<char>;
|
||||
|
||||
|
||||
/*! @brief Aliases for common character types. */
|
||||
using hashed_wstring = basic_hashed_string<wchar_t>;
|
||||
|
||||
|
||||
inline namespace literals {
|
||||
|
||||
|
||||
/**
|
||||
* @brief User defined literal for hashed strings.
|
||||
* @param str The literal without its suffix.
|
||||
* @return A properly initialized hashed string.
|
||||
*/
|
||||
[[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT {
|
||||
return entt::hashed_string{str};
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_CORE_HASHED_STRING_HPP
|
||||
/**
|
||||
* @brief User defined literal for hashed wstrings.
|
||||
* @param str The literal without its suffix.
|
||||
* @return A properly initialized hashed wstring.
|
||||
*/
|
||||
[[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT {
|
||||
return entt::hashed_wstring{str};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,72 +2,35 @@
|
||||
#define ENTT_CORE_IDENT_HPP
|
||||
|
||||
|
||||
#include<type_traits>
|
||||
#include<cstddef>
|
||||
#include<utility>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
#include "type_traits.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
template<typename... Types>
|
||||
struct Identifier final: Identifier<Types>... {
|
||||
using identifier_type = std::size_t;
|
||||
|
||||
template<std::size_t... Indexes>
|
||||
constexpr Identifier(std::index_sequence<Indexes...>)
|
||||
: Identifier<Types>{std::index_sequence<Indexes>{}}...
|
||||
{}
|
||||
|
||||
template<typename Type>
|
||||
constexpr std::size_t get() const {
|
||||
return Identifier<std::decay_t<Type>>::get();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename Type>
|
||||
struct Identifier<Type> {
|
||||
using identifier_type = std::size_t;
|
||||
|
||||
template<std::size_t Index>
|
||||
constexpr Identifier(std::index_sequence<Index>)
|
||||
: index{Index}
|
||||
{}
|
||||
|
||||
constexpr std::size_t get() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::size_t index;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Types identifers.
|
||||
* @brief Types identifiers.
|
||||
*
|
||||
* Variable template used to generate identifiers at compile-time for the given
|
||||
* types. Use the `constexpr` `get` member function to know what's the
|
||||
* identifier associated to the specific type.
|
||||
* types. Use the `get` member function to know what's the identifier associated
|
||||
* to the specific type.
|
||||
*
|
||||
* @note
|
||||
* Identifiers are constant expression and can be used in any context where such
|
||||
* an expression is required. As an example:
|
||||
* @code{.cpp}
|
||||
* constexpr auto identifiers = entt::ident<AType, AnotherType>;
|
||||
* using id = entt::identifier<a_type, another_type>;
|
||||
*
|
||||
* switch(aTypeIdentifier) {
|
||||
* case identifers.get<AType>():
|
||||
* switch(a_type_identifier) {
|
||||
* case id::type<a_type>:
|
||||
* // ...
|
||||
* break;
|
||||
* case identifers.get<AnotherType>():
|
||||
* case id::type<another_type>:
|
||||
* // ...
|
||||
* break;
|
||||
* default:
|
||||
@@ -75,22 +38,27 @@ private:
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @note
|
||||
* In case of single type list, `get` isn't a member function template:
|
||||
* @code{.cpp}
|
||||
* func(std::integral_constant<
|
||||
* entt::ident<AType>::identifier_type,
|
||||
* entt::ident<AType>::get()
|
||||
* >{});
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Types List of types for which to generate identifiers.
|
||||
*/
|
||||
template<typename... Types>
|
||||
constexpr auto ident = Identifier<std::decay_t<Types>...>{std::make_index_sequence<sizeof...(Types)>{}};
|
||||
class identifier {
|
||||
template<typename Type, std::size_t... Index>
|
||||
[[nodiscard]] static constexpr id_type get(std::index_sequence<Index...>) {
|
||||
static_assert(std::disjunction_v<std::is_same<Type, Types>...>, "Invalid type");
|
||||
return (0 + ... + (std::is_same_v<Type, type_list_element_t<Index, type_list<std::decay_t<Types>...>>> ? id_type{Index} : id_type{}));
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using identifier_type = id_type;
|
||||
|
||||
/*! @brief Statically generated unique identifier for the given type. */
|
||||
template<typename Type>
|
||||
static constexpr identifier_type type = get<std::decay_t<Type>>(std::index_sequence_for<Types...>{});
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_CORE_IDENT_HPP
|
||||
#endif
|
||||
|
||||
62
src/entt/core/monostate.hpp
Normal file
62
src/entt/core/monostate.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef ENTT_CORE_MONOSTATE_HPP
|
||||
#define ENTT_CORE_MONOSTATE_HPP
|
||||
|
||||
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Minimal implementation of the monostate pattern.
|
||||
*
|
||||
* A minimal, yet complete configuration system built on top of the monostate
|
||||
* pattern. Thread safe by design, it works only with basic types like `int`s or
|
||||
* `bool`s.<br/>
|
||||
* Multiple types and therefore more than one value can be associated with a
|
||||
* single key. Because of this, users must pay attention to use the same type
|
||||
* both during an assignment and when they try to read back their data.
|
||||
* Otherwise, they can incur in unexpected results.
|
||||
*/
|
||||
template<id_type>
|
||||
struct monostate {
|
||||
/**
|
||||
* @brief Assigns a value of a specific type to a given key.
|
||||
* @tparam Type Type of the value to assign.
|
||||
* @param val User data to assign to the given key.
|
||||
*/
|
||||
template<typename Type>
|
||||
void operator=(Type val) const ENTT_NOEXCEPT {
|
||||
value<Type> = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value of a specific type for a given key.
|
||||
* @tparam Type Type of the value to get.
|
||||
* @return Stored value, if any.
|
||||
*/
|
||||
template<typename Type>
|
||||
operator Type() const ENTT_NOEXCEPT {
|
||||
return value<Type>;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename Type>
|
||||
inline static ENTT_MAYBE_ATOMIC(Type) value{};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Value Value used to differentiate between different variables.
|
||||
*/
|
||||
template<id_type Value>
|
||||
inline monostate<Value> monostate_v = {};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
260
src/entt/core/type_info.hpp
Normal file
260
src/entt/core/type_info.hpp
Normal file
@@ -0,0 +1,260 @@
|
||||
#ifndef ENTT_CORE_TYPE_INFO_HPP
|
||||
#define ENTT_CORE_TYPE_INFO_HPP
|
||||
|
||||
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#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_seq final {
|
||||
[[nodiscard]] static id_type next() ENTT_NOEXCEPT {
|
||||
static ENTT_MAYBE_ATOMIC(id_type) value{};
|
||||
return value++;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT {
|
||||
#if defined ENTT_PRETTY_FUNCTION
|
||||
std::string_view pretty_function{ENTT_PRETTY_FUNCTION};
|
||||
auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX)+1);
|
||||
auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
|
||||
return value;
|
||||
#else
|
||||
return std::string_view{""};
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
|
||||
[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT {
|
||||
constexpr auto value = stripped_type_name<Type>();
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT {
|
||||
static const auto value = stripped_type_name<Type>();
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
|
||||
[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT {
|
||||
constexpr auto stripped = stripped_type_name<Type>();
|
||||
constexpr auto value = hashed_string::value(stripped.data(), stripped.size());
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT {
|
||||
static const auto value = [](const auto stripped) {
|
||||
return hashed_string::value(stripped.data(), stripped.size());
|
||||
}(stripped_type_name<Type>());
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Type sequential identifier.
|
||||
* @tparam Type Type for which to generate a sequential identifier.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
struct ENTT_API type_seq final {
|
||||
/**
|
||||
* @brief Returns the sequential identifier of a given type.
|
||||
* @return The sequential identifier of a given type.
|
||||
*/
|
||||
[[nodiscard]] static id_type value() ENTT_NOEXCEPT {
|
||||
static const id_type value = internal::type_seq::next();
|
||||
return value;
|
||||
}
|
||||
|
||||
/*! @copydoc value */
|
||||
[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Type hash.
|
||||
* @tparam Type Type for which to generate a hash value.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
struct type_hash final {
|
||||
/**
|
||||
* @brief Returns the numeric representation of a given type.
|
||||
* @return The numeric representation of the given type.
|
||||
*/
|
||||
#if defined ENTT_PRETTY_FUNCTION
|
||||
[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
|
||||
return internal::type_hash<Type>(0);
|
||||
#else
|
||||
[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
|
||||
return type_seq<Type>::value();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*! @copydoc value */
|
||||
[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Type name.
|
||||
* @tparam Type Type for which to generate a name.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
struct type_name final {
|
||||
/**
|
||||
* @brief Returns the name of a given type.
|
||||
* @return The name of the given type.
|
||||
*/
|
||||
[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT {
|
||||
return internal::type_name<Type>(0);
|
||||
}
|
||||
|
||||
/*! @copydoc value */
|
||||
[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); }
|
||||
};
|
||||
|
||||
|
||||
/*! @brief Implementation specific information about a type. */
|
||||
class type_info final {
|
||||
template<typename>
|
||||
friend type_info type_id() ENTT_NOEXCEPT;
|
||||
|
||||
type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT
|
||||
: seq_value{seq_v},
|
||||
hash_value{hash_v},
|
||||
name_value{name_v}
|
||||
{}
|
||||
|
||||
public:
|
||||
/*! @brief Default constructor. */
|
||||
type_info() ENTT_NOEXCEPT
|
||||
: type_info({}, {}, {})
|
||||
{}
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
type_info(const type_info &) ENTT_NOEXCEPT = default;
|
||||
/*! @brief Default move constructor. */
|
||||
type_info(type_info &&) ENTT_NOEXCEPT = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator.
|
||||
* @return This type info object.
|
||||
*/
|
||||
type_info & operator=(const type_info &) ENTT_NOEXCEPT = default;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @return This type info object.
|
||||
*/
|
||||
type_info & operator=(type_info &&) ENTT_NOEXCEPT = default;
|
||||
|
||||
/**
|
||||
* @brief Checks if a type info object is properly initialized.
|
||||
* @return True if the object is properly initialized, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return name_value.data() != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Type sequential identifier.
|
||||
* @return Type sequential identifier.
|
||||
*/
|
||||
[[nodiscard]] id_type seq() const ENTT_NOEXCEPT {
|
||||
return seq_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Type hash.
|
||||
* @return Type hash.
|
||||
*/
|
||||
[[nodiscard]] id_type hash() const ENTT_NOEXCEPT {
|
||||
return hash_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Type name.
|
||||
* @return Type name.
|
||||
*/
|
||||
[[nodiscard]] std::string_view name() const ENTT_NOEXCEPT {
|
||||
return name_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares the contents of two type info objects.
|
||||
* @param other Object with which to compare.
|
||||
* @return False if the two contents differ, true otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool operator==(const type_info &other) const ENTT_NOEXCEPT {
|
||||
return hash_value == other.hash_value;
|
||||
}
|
||||
|
||||
private:
|
||||
id_type seq_value;
|
||||
id_type hash_value;
|
||||
std::string_view name_value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Compares the contents of two type info objects.
|
||||
* @param lhs A type info object.
|
||||
* @param rhs A type info object.
|
||||
* @return True if the two contents differ, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] inline bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns the type info object for a given type.
|
||||
* @tparam Type Type for which to generate a type info object.
|
||||
* @return The type info object for the given type.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] type_info type_id() ENTT_NOEXCEPT {
|
||||
return type_info{
|
||||
type_seq<std::remove_cv_t<std::remove_reference_t<Type>>>::value(),
|
||||
type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value(),
|
||||
type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
669
src/entt/core/type_traits.hpp
Normal file
669
src/entt/core/type_traits.hpp
Normal file
@@ -0,0 +1,669 @@
|
||||
#ifndef ENTT_CORE_TYPE_TRAITS_HPP
|
||||
#define ENTT_CORE_TYPE_TRAITS_HPP
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Utility class to disambiguate overloaded functions.
|
||||
* @tparam N Number of choices available.
|
||||
*/
|
||||
template<std::size_t N>
|
||||
struct choice_t
|
||||
// Unfortunately, doxygen cannot parse such a construct.
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
: choice_t<N-1>
|
||||
/*! @endcond */
|
||||
{};
|
||||
|
||||
|
||||
/*! @copybrief choice_t */
|
||||
template<>
|
||||
struct choice_t<0> {};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Variable template for the choice trick.
|
||||
* @tparam N Number of choices available.
|
||||
*/
|
||||
template<std::size_t N>
|
||||
inline constexpr choice_t<N> choice{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Identity type trait.
|
||||
*
|
||||
* Useful to establish non-deduced contexts in template argument deduction
|
||||
* (waiting for C++20) or to provide types through function arguments.
|
||||
*
|
||||
* @tparam Type A type.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct type_identity {
|
||||
/*! @brief Identity type. */
|
||||
using type = Type;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Type A type.
|
||||
*/
|
||||
template<typename Type>
|
||||
using type_identity_t = typename type_identity<Type>::type;
|
||||
|
||||
|
||||
/**
|
||||
* @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
|
||||
* @tparam Type The type of which to return the size.
|
||||
* @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
struct size_of: std::integral_constant<std::size_t, 0u> {};
|
||||
|
||||
|
||||
/*! @copydoc size_of */
|
||||
template<typename Type>
|
||||
struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
|
||||
: std::integral_constant<std::size_t, sizeof(Type)>
|
||||
{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The type of which to return the size.
|
||||
*/
|
||||
template<class Type>
|
||||
inline constexpr auto size_of_v = size_of<Type>::value;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Using declaration to be used to _repeat_ the same type a number of
|
||||
* times equal to the size of a given parameter pack.
|
||||
* @tparam Type A type to repeat.
|
||||
*/
|
||||
template<typename Type, typename>
|
||||
using unpack_as_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 facilitate the creation of named values.
|
||||
* @tparam Value A constant value at least convertible to `id_type`.
|
||||
*/
|
||||
template<id_type Value>
|
||||
using tag = integral_constant<Value>;
|
||||
|
||||
|
||||
/**
|
||||
* @brief A class to use to push around lists of types, nothing more.
|
||||
* @tparam Type Types provided by the type list.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct type_list {
|
||||
/*! @brief Type list type. */
|
||||
using type = type_list;
|
||||
/*! @brief Compile-time number of elements in the type list. */
|
||||
static constexpr auto size = sizeof...(Type);
|
||||
};
|
||||
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<std::size_t, typename>
|
||||
struct type_list_element;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time indexed access to the types of a type list.
|
||||
* @tparam Index Index of the type to return.
|
||||
* @tparam Type First type provided by the type list.
|
||||
* @tparam Other Other types provided by the type list.
|
||||
*/
|
||||
template<std::size_t Index, typename Type, typename... Other>
|
||||
struct type_list_element<Index, type_list<Type, Other...>>
|
||||
: type_list_element<Index - 1u, type_list<Other...>>
|
||||
{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time indexed access to the types of a type list.
|
||||
* @tparam Type First type provided by the type list.
|
||||
* @tparam Other Other types provided by the type list.
|
||||
*/
|
||||
template<typename Type, typename... Other>
|
||||
struct type_list_element<0u, type_list<Type, Other...>> {
|
||||
/*! @brief Searched type. */
|
||||
using type = Type;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Index Index of the type to return.
|
||||
* @tparam List Type list to search into.
|
||||
*/
|
||||
template<std::size_t Index, typename List>
|
||||
using type_list_element_t = typename type_list_element<Index, List>::type;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple type lists.
|
||||
* @tparam Type Types provided by the first type list.
|
||||
* @tparam Other Types provided by the second type list.
|
||||
* @return A type list composed by the types of both the type lists.
|
||||
*/
|
||||
template<typename... Type, typename... Other>
|
||||
constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { return {}; }
|
||||
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename...>
|
||||
struct type_list_cat;
|
||||
|
||||
|
||||
/*! @brief Concatenates multiple type lists. */
|
||||
template<>
|
||||
struct type_list_cat<> {
|
||||
/*! @brief A type list composed by the types of all the type lists. */
|
||||
using type = type_list<>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple type lists.
|
||||
* @tparam Type Types provided by the first type list.
|
||||
* @tparam Other Types provided by the second type list.
|
||||
* @tparam List Other type lists, if any.
|
||||
*/
|
||||
template<typename... Type, typename... Other, typename... List>
|
||||
struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
|
||||
/*! @brief A type list composed by the types of all the type lists. */
|
||||
using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple type lists.
|
||||
* @tparam Type Types provided by the type list.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct type_list_cat<type_list<Type...>> {
|
||||
/*! @brief A type list composed by the types of all the type lists. */
|
||||
using type = type_list<Type...>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam List Type lists to concatenate.
|
||||
*/
|
||||
template<typename... List>
|
||||
using type_list_cat_t = typename type_list_cat<List...>::type;
|
||||
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename>
|
||||
struct type_list_unique;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Removes duplicates types from a type list.
|
||||
* @tparam Type One of the types provided by the given type list.
|
||||
* @tparam Other The other types provided by the given type list.
|
||||
*/
|
||||
template<typename Type, typename... Other>
|
||||
struct type_list_unique<type_list<Type, Other...>> {
|
||||
/*! @brief A type list without duplicate types. */
|
||||
using type = std::conditional_t<
|
||||
std::disjunction_v<std::is_same<Type, Other>...>,
|
||||
typename type_list_unique<type_list<Other...>>::type,
|
||||
type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>
|
||||
>;
|
||||
};
|
||||
|
||||
|
||||
/*! @brief Removes duplicates types from a type list. */
|
||||
template<>
|
||||
struct type_list_unique<type_list<>> {
|
||||
/*! @brief A type list without duplicate types. */
|
||||
using type = type_list<>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Type A type list.
|
||||
*/
|
||||
template<typename Type>
|
||||
using type_list_unique_t = typename type_list_unique<Type>::type;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a type list contains a
|
||||
* given type, false otherwise.
|
||||
* @tparam List Type list.
|
||||
* @tparam Type Type to look for.
|
||||
*/
|
||||
template<typename List, typename Type>
|
||||
struct type_list_contains;
|
||||
|
||||
|
||||
/**
|
||||
* @copybrief type_list_contains
|
||||
* @tparam Type Types provided by the type list.
|
||||
* @tparam Other Type to look for.
|
||||
*/
|
||||
template<typename... Type, typename Other>
|
||||
struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam List Type list.
|
||||
* @tparam Type Type to look for.
|
||||
*/
|
||||
template<class List, typename Type>
|
||||
inline constexpr auto type_list_contains_v = type_list_contains<List, Type>::value;
|
||||
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename...>
|
||||
struct type_list_diff;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Computes the difference between two type lists.
|
||||
* @tparam Type Types provided by the first type list.
|
||||
* @tparam Other Types provided by the second type list.
|
||||
*/
|
||||
template<typename... Type, typename... Other>
|
||||
struct type_list_diff<type_list<Type...>, type_list<Other...>> {
|
||||
/*! @brief A type list that is the difference between the two type lists. */
|
||||
using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam List Type lists between which to compute the difference.
|
||||
*/
|
||||
template<typename... List>
|
||||
using type_list_diff_t = typename type_list_diff<List...>::type;
|
||||
|
||||
|
||||
/**
|
||||
* @brief A class to use to push around lists of constant values, nothing more.
|
||||
* @tparam Value Values provided by the value list.
|
||||
*/
|
||||
template<auto... Value>
|
||||
struct value_list {
|
||||
/*! @brief Value list type. */
|
||||
using type = value_list;
|
||||
/*! @brief Compile-time number of elements in the value list. */
|
||||
static constexpr auto size = sizeof...(Value);
|
||||
};
|
||||
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<std::size_t, typename>
|
||||
struct value_list_element;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time indexed access to the values of a value list.
|
||||
* @tparam Index Index of the value to return.
|
||||
* @tparam Value First value provided by the value list.
|
||||
* @tparam Other Other values provided by the value list.
|
||||
*/
|
||||
template<std::size_t Index, auto Value, auto... Other>
|
||||
struct value_list_element<Index, value_list<Value, Other...>>
|
||||
: value_list_element<Index - 1u, value_list<Other...>>
|
||||
{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time indexed access to the types of a type list.
|
||||
* @tparam Value First value provided by the value list.
|
||||
* @tparam Other Other values provided by the value list.
|
||||
*/
|
||||
template<auto Value, auto... Other>
|
||||
struct value_list_element<0u, value_list<Value, Other...>> {
|
||||
/*! @brief Searched value. */
|
||||
static constexpr auto value = Value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Index Index of the value to return.
|
||||
* @tparam List Value list to search into.
|
||||
*/
|
||||
template<std::size_t Index, typename List>
|
||||
inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple value lists.
|
||||
* @tparam Value Values provided by the first value list.
|
||||
* @tparam Other Values provided by the second value list.
|
||||
* @return A value list composed by the values of both the value lists.
|
||||
*/
|
||||
template<auto... Value, auto... Other>
|
||||
constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { return {}; }
|
||||
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename...>
|
||||
struct value_list_cat;
|
||||
|
||||
|
||||
/*! @brief Concatenates multiple value lists. */
|
||||
template<>
|
||||
struct value_list_cat<> {
|
||||
/*! @brief A value list composed by the values of all the value lists. */
|
||||
using type = value_list<>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple value lists.
|
||||
* @tparam Value Values provided by the first value list.
|
||||
* @tparam Other Values provided by the second value list.
|
||||
* @tparam List Other value lists, if any.
|
||||
*/
|
||||
template<auto... Value, auto... Other, typename... List>
|
||||
struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
|
||||
/*! @brief A value list composed by the values of all the value lists. */
|
||||
using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple value lists.
|
||||
* @tparam Value Values provided by the value list.
|
||||
*/
|
||||
template<auto... Value>
|
||||
struct value_list_cat<value_list<Value...>> {
|
||||
/*! @brief A value list composed by the values of all the value lists. */
|
||||
using type = value_list<Value...>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam List Value lists to concatenate.
|
||||
*/
|
||||
template<typename... List>
|
||||
using value_list_cat_t = typename value_list_cat<List...>::type;
|
||||
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
|
||||
namespace internal {
|
||||
|
||||
|
||||
template<typename>
|
||||
[[nodiscard]] constexpr bool is_equality_comparable(...) { return false; }
|
||||
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>)
|
||||
-> decltype(std::declval<Type>() == std::declval<Type>()) { return true; }
|
||||
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>)
|
||||
-> decltype(std::declval<typename Type::value_type>(), std::declval<Type>() == std::declval<Type>()) {
|
||||
return is_equality_comparable<typename Type::value_type>(choice<2>);
|
||||
}
|
||||
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>)
|
||||
-> decltype(std::declval<typename Type::mapped_type>(), std::declval<Type>() == std::declval<Type>()) {
|
||||
return is_equality_comparable<typename Type::key_type>(choice<2>) && is_equality_comparable<typename Type::mapped_type>(choice<2>);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type is
|
||||
* equality comparable, false otherwise.
|
||||
* @tparam Type Potentially equality comparable type.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
struct is_equality_comparable: std::bool_constant<internal::is_equality_comparable<Type>(choice<2>)> {};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type Potentially equality comparable type.
|
||||
*/
|
||||
template<class Type>
|
||||
inline constexpr auto is_equality_comparable_v = is_equality_comparable<Type>::value;
|
||||
|
||||
|
||||
/*! @brief Same as std::is_invocable, but with tuples. */
|
||||
template<typename, typename>
|
||||
struct is_applicable: std::false_type {};
|
||||
|
||||
|
||||
/**
|
||||
* @copybrief is_applicable
|
||||
* @tparam Func A valid function type.
|
||||
* @tparam Tuple Tuple-like type.
|
||||
* @tparam Args The list of arguments to use to probe the function type.
|
||||
*/
|
||||
template<typename Func, template<typename...> class Tuple, typename... Args>
|
||||
struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
|
||||
|
||||
|
||||
/**
|
||||
* @copybrief is_applicable
|
||||
* @tparam Func A valid function type.
|
||||
* @tparam Tuple Tuple-like type.
|
||||
* @tparam Args The list of arguments to use to probe the function type.
|
||||
*/
|
||||
template<typename Func, template<typename...> class Tuple, typename... Args>
|
||||
struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Func A valid function type.
|
||||
* @tparam Args The list of arguments to use to probe the function type.
|
||||
*/
|
||||
template<typename Func, typename Args>
|
||||
inline constexpr auto is_applicable_v = is_applicable<Func, Args>::value;
|
||||
|
||||
|
||||
/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */
|
||||
template<typename, typename, typename>
|
||||
struct is_applicable_r: std::false_type {};
|
||||
|
||||
|
||||
/**
|
||||
* @copybrief is_applicable_r
|
||||
* @tparam Ret The type to which the return type of the function should be
|
||||
* convertible.
|
||||
* @tparam Func A valid function type.
|
||||
* @tparam Args The list of arguments to use to probe the function type.
|
||||
*/
|
||||
template<typename Ret, typename Func, typename... Args>
|
||||
struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Ret The type to which the return type of the function should be
|
||||
* convertible.
|
||||
* @tparam Func A valid function type.
|
||||
* @tparam Args The list of arguments to use to probe the function type.
|
||||
*/
|
||||
template<typename Ret, typename Func, typename Args>
|
||||
inline constexpr auto is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type is
|
||||
* complete, false otherwise.
|
||||
* @tparam Type Potential complete type.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
struct is_complete: std::false_type {};
|
||||
|
||||
|
||||
/*! @copydoc is_complete */
|
||||
template<typename Type>
|
||||
struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type Potential complete type.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr auto is_complete_v = is_complete<Type>::value;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type is
|
||||
* hashable, false otherwise.
|
||||
* @tparam Type Potentially hashable type.
|
||||
*/
|
||||
template <typename Type, typename = void>
|
||||
struct is_std_hashable: std::false_type {};
|
||||
|
||||
|
||||
/*! @copydoc is_std_hashable */
|
||||
template <typename Type>
|
||||
struct is_std_hashable<Type, std::enable_if_t<std::is_convertible_v<decltype(std::declval<std::hash<Type>>()(std::declval<Type>())), std::size_t>>>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type Potentially hashable type.
|
||||
*/
|
||||
template <typename Type>
|
||||
inline constexpr auto is_std_hashable_v = is_std_hashable<Type>::value;
|
||||
|
||||
|
||||
/**
|
||||
* @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 Type, typename = void>
|
||||
struct is_empty: ENTT_IS_EMPTY(Type) {};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type Potential empty type.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr auto is_empty_v = is_empty<Type>::value;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Transcribes the constness of a type to another type.
|
||||
* @tparam To The type to which to transcribe the constness.
|
||||
* @tparam From The type from which to transcribe the constness.
|
||||
*/
|
||||
template<typename To, typename From>
|
||||
struct constness_as {
|
||||
/*! @brief The type resulting from the transcription of the constness. */
|
||||
using type = std::remove_const_t<To>;
|
||||
};
|
||||
|
||||
|
||||
/*! @copydoc constness_as */
|
||||
template<typename To, typename From>
|
||||
struct constness_as<To, const From> {
|
||||
/*! @brief The type resulting from the transcription of the constness. */
|
||||
using type = std::add_const_t<To>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Alias template to facilitate the transcription of the constness.
|
||||
* @tparam To The type to which to transcribe the constness.
|
||||
* @tparam From The type from which to transcribe the constness.
|
||||
*/
|
||||
template<typename To, typename From>
|
||||
using constness_as_t = typename constness_as<To, From>::type;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Extracts the class of a non-static member object or function.
|
||||
* @tparam Member A pointer to a non-static member object or function.
|
||||
*/
|
||||
template<typename Member>
|
||||
class member_class {
|
||||
static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function");
|
||||
|
||||
template<typename Class, typename Ret, typename... Args>
|
||||
static Class * clazz(Ret(Class:: *)(Args...));
|
||||
|
||||
template<typename Class, typename Ret, typename... Args>
|
||||
static Class * clazz(Ret(Class:: *)(Args...) const);
|
||||
|
||||
template<typename Class, typename Type>
|
||||
static Class * clazz(Type Class:: *);
|
||||
|
||||
public:
|
||||
/*! @brief The class of the given non-static member object or function. */
|
||||
using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Member A pointer to a non-static member object or function.
|
||||
*/
|
||||
template<typename Member>
|
||||
using member_class_t = typename member_class<Member>::type;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
105
src/entt/core/utility.hpp
Normal file
105
src/entt/core/utility.hpp
Normal file
@@ -0,0 +1,105 @@
|
||||
#ifndef ENTT_CORE_UTILITY_HPP
|
||||
#define ENTT_CORE_UTILITY_HPP
|
||||
|
||||
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/*! @brief Identity function object (waiting for C++20). */
|
||||
struct identity {
|
||||
/**
|
||||
* @brief Returns its argument unchanged.
|
||||
* @tparam Type Type of the argument.
|
||||
* @param value The actual argument.
|
||||
* @return The submitted value as-is.
|
||||
*/
|
||||
template<class Type>
|
||||
[[nodiscard]] constexpr Type && operator()(Type &&value) const ENTT_NOEXCEPT {
|
||||
return std::forward<Type>(value);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Constant utility to disambiguate overloaded members of a class.
|
||||
* @tparam Type Type of the desired overload.
|
||||
* @tparam Class Type of class to which the member belongs.
|
||||
* @param member A valid pointer to a member.
|
||||
* @return Pointer to the member.
|
||||
*/
|
||||
template<typename Type, typename Class>
|
||||
[[nodiscard]] constexpr auto overload(Type Class:: *member) ENTT_NOEXCEPT { return member; }
|
||||
|
||||
|
||||
/**
|
||||
* @brief Constant utility to disambiguate overloaded functions.
|
||||
* @tparam Func Function type of the desired overload.
|
||||
* @param func A valid pointer to a function.
|
||||
* @return Pointer to the function.
|
||||
*/
|
||||
template<typename Func>
|
||||
[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { return func; }
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper type for visitors.
|
||||
* @tparam Func Types of function objects.
|
||||
*/
|
||||
template<class... Func>
|
||||
struct overloaded: Func... {
|
||||
using Func::operator()...;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Func Types of function objects.
|
||||
*/
|
||||
template<class... Func>
|
||||
overloaded(Func...) -> overloaded<Func...>;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Basic implementation of a y-combinator.
|
||||
* @tparam Func Type of a potentially recursive function.
|
||||
*/
|
||||
template<class Func>
|
||||
struct y_combinator {
|
||||
/**
|
||||
* @brief Constructs a y-combinator from a given function.
|
||||
* @param recursive A potentially recursive function.
|
||||
*/
|
||||
y_combinator(Func recursive):
|
||||
func{std::move(recursive)}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Invokes a y-combinator and therefore its underlying function.
|
||||
* @tparam Args Types of arguments to use to invoke the underlying function.
|
||||
* @param args Parameters to use to invoke the underlying function.
|
||||
* @return Return value of the underlying function, if any.
|
||||
*/
|
||||
template <class... Args>
|
||||
decltype(auto) operator()(Args &&... args) const {
|
||||
return func(*this, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/*! @copydoc operator()() */
|
||||
template <class... Args>
|
||||
decltype(auto) operator()(Args &&... args) {
|
||||
return func(*this, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
Func func;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,154 +0,0 @@
|
||||
#ifndef ENTT_ENTITY_ACTOR_HPP
|
||||
#define ENTT_ENTITY_ACTOR_HPP
|
||||
|
||||
|
||||
#include <utility>
|
||||
#include "registry.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Dedicated to those who aren't confident with entity-component systems.
|
||||
*
|
||||
* Tiny wrapper around a registry, for all those users that aren't confident
|
||||
* with entity-component systems and prefer to iterate objects directly.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Delta Type to use to provide elapsed time.
|
||||
*/
|
||||
template<typename Entity, typename Delta>
|
||||
struct Actor {
|
||||
/*! @brief Type of registry used internally. */
|
||||
using registry_type = Registry<Entity>;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Type used to provide elapsed time. */
|
||||
using delta_type = Delta;
|
||||
|
||||
/**
|
||||
* @brief Constructs an actor by using the given registry.
|
||||
* @param reg An entity-component system properly initialized.
|
||||
*/
|
||||
Actor(Registry<Entity> ®)
|
||||
: reg{reg}, entity{reg.create()}
|
||||
{}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
virtual ~Actor() {
|
||||
reg.destroy(entity);
|
||||
}
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
Actor(const Actor &) = default;
|
||||
/*! @brief Default move constructor. */
|
||||
Actor(Actor &&) = default;
|
||||
|
||||
/*! @brief Default copy assignment operator. @return This actor. */
|
||||
Actor & operator=(const Actor &) = default;
|
||||
/*! @brief Default move assignment operator. @return This actor. */
|
||||
Actor & operator=(Actor &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Assigns the given component to an actor.
|
||||
*
|
||||
* A new instance of the given component is created and initialized with the
|
||||
* arguments provided (the component must have a proper constructor or be of
|
||||
* aggregate type). Then the component is assigned to the actor.<br/>
|
||||
* In case the actor already has a component of the given type, it's
|
||||
* replaced with the new one.
|
||||
*
|
||||
* @tparam Component Type of the 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>
|
||||
Component & set(Args&&... args) {
|
||||
return reg.template accomodate<Component>(entity, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the given component from an actor.
|
||||
* @tparam Component Type of the component to remove.
|
||||
*/
|
||||
template<typename Component>
|
||||
void unset() {
|
||||
reg.template remove<Component>(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if an actor has the given component.
|
||||
* @tparam Component Type of the component for which to perform the check.
|
||||
* @return True if the actor has the component, false otherwise.
|
||||
*/
|
||||
template<typename Component>
|
||||
bool has() const noexcept {
|
||||
return reg.template has<Component>(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the given component for an actor.
|
||||
* @tparam Component Type of the component to get.
|
||||
* @return A reference to the instance of the component owned by the entity.
|
||||
*/
|
||||
template<typename Component>
|
||||
const Component & get() const noexcept {
|
||||
return reg.template get<Component>(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the given component for an actor.
|
||||
* @tparam Component Type of the component to get.
|
||||
* @return A reference to the instance of the component owned by the entity.
|
||||
*/
|
||||
template<typename Component>
|
||||
Component & get() noexcept {
|
||||
return const_cast<Component &>(const_cast<const Actor *>(this)->get<Component>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the underlying registry.
|
||||
* @return A reference to the underlying registry
|
||||
*/
|
||||
const registry_type & registry() const noexcept {
|
||||
return reg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the underlying registry.
|
||||
* @return A reference to the underlying registry
|
||||
*/
|
||||
registry_type & registry() noexcept {
|
||||
return const_cast<registry_type &>(const_cast<const Actor *>(this)->registry());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates an actor, whatever it means to update it.
|
||||
* @param delta Elapsed time.
|
||||
*/
|
||||
virtual void update(delta_type delta) = 0;
|
||||
|
||||
private:
|
||||
registry_type ®
|
||||
Entity entity;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Default actor class.
|
||||
*
|
||||
* The default actor is the best choice for almost all the applications.<br/>
|
||||
* Users should have a really good reason to choose something different.
|
||||
*
|
||||
* @tparam Delta Type to use to provide elapsed time.
|
||||
*/
|
||||
template<typename Delta>
|
||||
using DefaultActor = Actor<std::uint32_t, Delta>;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_ENTITY_ACTOR_HPP
|
||||
195
src/entt/entity/entity.hpp
Normal file
195
src/entt/entity/entity.hpp
Normal file
@@ -0,0 +1,195 @@
|
||||
#ifndef ENTT_ENTITY_ENTITY_HPP
|
||||
#define ENTT_ENTITY_ENTITY_HPP
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Entity traits.
|
||||
*
|
||||
* 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, 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 32 bits entity identifier.
|
||||
*
|
||||
* A 32 bits entity identifier guarantees:
|
||||
*
|
||||
* * 20 bits for the entity number (suitable for almost all the games).
|
||||
* * 12 bit for the version (resets in [0-4095]).
|
||||
*/
|
||||
template<>
|
||||
struct entt_traits<std::uint32_t> {
|
||||
/*! @brief Underlying entity type. */
|
||||
using entity_type = std::uint32_t;
|
||||
/*! @brief Underlying version type. */
|
||||
using version_type = std::uint16_t;
|
||||
/*! @brief Difference type. */
|
||||
using difference_type = std::int64_t;
|
||||
|
||||
/*! @brief Mask to use to get the entity number out of an identifier. */
|
||||
static constexpr entity_type entity_mask = 0xFFFFF;
|
||||
/*! @brief Mask to use to get the version out of an identifier. */
|
||||
static constexpr entity_type version_mask = 0xFFF;
|
||||
/*! @brief Extent of the entity number within an identifier. */
|
||||
static constexpr std::size_t entity_shift = 20u;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Entity traits for a 64 bits entity identifier.
|
||||
*
|
||||
* A 64 bits entity identifier guarantees:
|
||||
*
|
||||
* * 32 bits for the entity number (an indecently large number).
|
||||
* * 32 bit for the version (an indecently large number).
|
||||
*/
|
||||
template<>
|
||||
struct entt_traits<std::uint64_t> {
|
||||
/*! @brief Underlying entity type. */
|
||||
using entity_type = std::uint64_t;
|
||||
/*! @brief Underlying version type. */
|
||||
using version_type = std::uint32_t;
|
||||
/*! @brief Difference type. */
|
||||
using difference_type = std::int64_t;
|
||||
|
||||
/*! @brief Mask to use to get the entity number out of an identifier. */
|
||||
static constexpr entity_type entity_mask = 0xFFFFFFFF;
|
||||
/*! @brief Mask to use to get the version out of an identifier. */
|
||||
static constexpr entity_type version_mask = 0xFFFFFFFF;
|
||||
/*! @brief Extent of the entity number within an identifier. */
|
||||
static constexpr std::size_t entity_shift = 32u;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
||||
/*! @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>
|
||||
[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT {
|
||||
return Entity{entt_traits<Entity>::entity_mask};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two null objects.
|
||||
* @return True in all cases.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator==(const null_t &) const ENTT_NOEXCEPT {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares two null objects.
|
||||
* @return False in all cases.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator!=(const 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>
|
||||
[[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>
|
||||
[[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>
|
||||
[[nodiscard]] constexpr bool operator==(const Entity &entity, const 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>
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity &entity, const null_t &other) ENTT_NOEXCEPT {
|
||||
return !(other == entity);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Compile-time constant for null entities.
|
||||
*
|
||||
* There exist implicit conversions from this variable to entity identifiers of
|
||||
* any allowed type. Similarly, there exist comparision operators between the
|
||||
* null entity and any other entity identifier.
|
||||
*/
|
||||
inline constexpr null_t null{};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,96 +0,0 @@
|
||||
#ifndef ENTT_ENTITY_ENTT_TRAITS_HPP
|
||||
#define ENTT_ENTITY_ENTT_TRAITS_HPP
|
||||
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Entity traits.
|
||||
*
|
||||
* 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>
|
||||
struct entt_traits;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Entity traits for a 16 bits entity identifier.
|
||||
*
|
||||
* A 16 bits entity identifier guarantees:
|
||||
*
|
||||
* * 12 bits for the entity number (up to 4k entities).
|
||||
* * 4 bit for the version (resets in [0-15]).
|
||||
*/
|
||||
template<>
|
||||
struct entt_traits<std::uint16_t> {
|
||||
/*! @brief Underlying entity type. */
|
||||
using entity_type = std::uint16_t;
|
||||
/*! @brief Underlying version type. */
|
||||
using version_type = std::uint8_t;
|
||||
|
||||
/*! @brief Mask to use to get the entity number out of an identifier. */
|
||||
static constexpr auto entity_mask = 0xFFF;
|
||||
/*! @brief Mask to use to get the version out of an identifier. */
|
||||
static constexpr auto version_mask = 0xF;
|
||||
/*! @brief Extent of the entity number within an identifier. */
|
||||
static constexpr auto entity_shift = 12;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Entity traits for a 32 bits entity identifier.
|
||||
*
|
||||
* A 32 bits entity identifier guarantees:
|
||||
*
|
||||
* * 24 bits for the entity number (suitable for almost all the games).
|
||||
* * 8 bit for the version (resets in [0-255]).
|
||||
*/
|
||||
template<>
|
||||
struct entt_traits<std::uint32_t> {
|
||||
/*! @brief Underlying entity type. */
|
||||
using entity_type = std::uint32_t;
|
||||
/*! @brief Underlying version type. */
|
||||
using version_type = std::uint16_t;
|
||||
|
||||
/*! @brief Mask to use to get the entity number out of an identifier. */
|
||||
static constexpr auto entity_mask = 0xFFFFFF;
|
||||
/*! @brief Mask to use to get the version out of an identifier. */
|
||||
static constexpr auto version_mask = 0xFF;
|
||||
/*! @brief Extent of the entity number within an identifier. */
|
||||
static constexpr auto entity_shift = 24;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Entity traits for a 64 bits entity identifier.
|
||||
*
|
||||
* A 64 bits entity identifier guarantees:
|
||||
*
|
||||
* * 40 bits for the entity number (an indecently large number).
|
||||
* * 24 bit for the version (an indecently large number).
|
||||
*/
|
||||
template<>
|
||||
struct entt_traits<std::uint64_t> {
|
||||
/*! @brief Underlying entity type. */
|
||||
using entity_type = std::uint64_t;
|
||||
/*! @brief Underlying version type. */
|
||||
using version_type = std::uint32_t;
|
||||
|
||||
/*! @brief Mask to use to get the entity number out of an identifier. */
|
||||
static constexpr auto entity_mask = 0xFFFFFFFFFF;
|
||||
/*! @brief Mask to use to get the version out of an identifier. */
|
||||
static constexpr auto version_mask = 0xFFFFFF;
|
||||
/*! @brief Extent of the entity number within an identifier. */
|
||||
static constexpr auto entity_shift = 40;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_ENTITY_ENTT_TRAITS_HPP
|
||||
146
src/entt/entity/fwd.hpp
Normal file
146
src/entt/entity/fwd.hpp
Normal file
@@ -0,0 +1,146 @@
|
||||
#ifndef ENTT_ENTITY_FWD_HPP
|
||||
#define ENTT_ENTITY_FWD_HPP
|
||||
|
||||
|
||||
#include "../core/fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
template<typename>
|
||||
class basic_sparse_set;
|
||||
|
||||
|
||||
template<typename, typename, typename>
|
||||
class basic_storage;
|
||||
|
||||
|
||||
template<typename>
|
||||
class basic_registry;
|
||||
|
||||
|
||||
template<typename...>
|
||||
class basic_view;
|
||||
|
||||
|
||||
template<typename>
|
||||
class basic_runtime_view;
|
||||
|
||||
|
||||
template<typename...>
|
||||
class basic_group;
|
||||
|
||||
|
||||
template<typename>
|
||||
class basic_observer;
|
||||
|
||||
|
||||
template<typename>
|
||||
class basic_organizer;
|
||||
|
||||
|
||||
template<typename, typename...>
|
||||
struct basic_handle;
|
||||
|
||||
|
||||
template<typename>
|
||||
class basic_snapshot;
|
||||
|
||||
|
||||
template<typename>
|
||||
class basic_snapshot_loader;
|
||||
|
||||
|
||||
template<typename>
|
||||
class basic_continuous_loader;
|
||||
|
||||
|
||||
/*! @brief Default entity identifier. */
|
||||
enum class entity: id_type {};
|
||||
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using sparse_set = basic_sparse_set<entity>;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using storage = basic_storage<entity, Args...>;
|
||||
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using registry = basic_registry<entity>;
|
||||
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using observer = basic_observer<entity>;
|
||||
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using organizer = basic_organizer<entity>;
|
||||
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using handle = basic_handle<entity>;
|
||||
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using const_handle = basic_handle<const entity>;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using handle_view = basic_handle<entity, Args...>;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using const_handle_view = basic_handle<const entity, Args...>;
|
||||
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using snapshot = basic_snapshot<entity>;
|
||||
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using snapshot_loader = basic_snapshot_loader<entity>;
|
||||
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using continuous_loader = basic_continuous_loader<entity>;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using view = basic_view<entity, Args...>;
|
||||
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using runtime_view = basic_runtime_view<entity>;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using group = basic_group<entity, Args...>;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
975
src/entt/entity/group.hpp
Normal file
975
src/entt/entity/group.hpp
Normal file
@@ -0,0 +1,975 @@
|
||||
#ifndef ENTT_ENTITY_GROUP_HPP
|
||||
#define ENTT_ENTITY_GROUP_HPP
|
||||
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "sparse_set.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Group.
|
||||
*
|
||||
* Primary template isn't defined on purpose. All the specializations give a
|
||||
* compile-time error, but for a few reasonable cases.
|
||||
*/
|
||||
template<typename...>
|
||||
class basic_group;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Non-owning group.
|
||||
*
|
||||
* A non-owning group returns all entities and only the entities that have at
|
||||
* least the given components. Moreover, it's guaranteed that the entity list
|
||||
* is tightly packed in memory for fast iterations.
|
||||
*
|
||||
* @b Important
|
||||
*
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all other cases, modifying the pools iterated by the group in any way
|
||||
* invalidates all the iterators and using them results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Groups share references to the underlying data structures of the registry
|
||||
* that generated them. Therefore any change to the entities and to the
|
||||
* components made by means of the registry are immediately reflected by all the
|
||||
* groups.<br/>
|
||||
* Moreover, sorting a non-owning group affects all the instances of the same
|
||||
* group (it means that users don't have to call `sort` on each instance to sort
|
||||
* all of them because they _share_ entities and components).
|
||||
*
|
||||
* @warning
|
||||
* Lifetime of a group must not overcome that of the registry that generated it.
|
||||
* In any other case, attempting to use a group results in undefined behavior.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Exclude Types of components used to filter the group.
|
||||
* @tparam Get Type of components observed by the group.
|
||||
*/
|
||||
template<typename Entity, typename... Exclude, typename... Get>
|
||||
class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>> final {
|
||||
/*! @brief A registry is allowed to create groups. */
|
||||
friend class basic_registry<Entity>;
|
||||
|
||||
template<typename Component>
|
||||
using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Component>>::storage_type, Component>;
|
||||
|
||||
class iterable_group final {
|
||||
friend class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>>;
|
||||
|
||||
template<typename It>
|
||||
class iterable_group_iterator final {
|
||||
friend class iterable_group;
|
||||
|
||||
template<typename... Args>
|
||||
iterable_group_iterator(It from, const std::tuple<storage_type<Get> *...> &args) ENTT_NOEXCEPT
|
||||
: it{from},
|
||||
pools{args}
|
||||
{}
|
||||
|
||||
public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
|
||||
using pointer = void;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
iterable_group_iterator & operator++() ENTT_NOEXCEPT {
|
||||
return ++it, *this;
|
||||
}
|
||||
|
||||
iterable_group_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
iterable_group_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
|
||||
const auto entt = *it;
|
||||
return std::tuple_cat(std::make_tuple(entt), get_as_tuple(*std::get<storage_type<Get> *>(pools), entt)...);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const iterable_group_iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.it == it;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator!=(const iterable_group_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
It it;
|
||||
const std::tuple<storage_type<Get> *...> pools;
|
||||
};
|
||||
|
||||
iterable_group(basic_sparse_set<Entity> * const ref, const std::tuple<storage_type<Get> *...> &cpools)
|
||||
: handler{ref},
|
||||
pools{cpools}
|
||||
{}
|
||||
|
||||
public:
|
||||
using iterator = iterable_group_iterator<typename basic_sparse_set<Entity>::iterator>;
|
||||
using reverse_iterator = iterable_group_iterator<typename basic_sparse_set<Entity>::reverse_iterator>;
|
||||
|
||||
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
|
||||
return handler ? iterator{handler->begin(), pools} : iterator{{}, pools};
|
||||
}
|
||||
|
||||
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
|
||||
return handler ? iterator{handler->end(), pools} : iterator{{}, pools};
|
||||
}
|
||||
|
||||
[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
|
||||
return handler ? reverse_iterator{handler->rbegin(), pools} : reverse_iterator{{}, pools};
|
||||
}
|
||||
|
||||
[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
|
||||
return handler ? reverse_iterator{handler->rend(), pools} : reverse_iterator{{}, pools};
|
||||
}
|
||||
|
||||
private:
|
||||
basic_sparse_set<Entity> * const handler;
|
||||
const std::tuple<storage_type<Get> *...> pools;
|
||||
};
|
||||
|
||||
basic_group(basic_sparse_set<Entity> &ref, storage_type<Get> &... gpool) ENTT_NOEXCEPT
|
||||
: handler{&ref},
|
||||
pools{&gpool...}
|
||||
{}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = typename basic_sparse_set<Entity>::iterator;
|
||||
/*! @brief Reversed iterator type. */
|
||||
using reverse_iterator = typename basic_sparse_set<Entity>::reverse_iterator;
|
||||
|
||||
/*! @brief Default constructor to use to create empty, invalid groups. */
|
||||
basic_group() ENTT_NOEXCEPT
|
||||
: handler{}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of entities that have the given components.
|
||||
* @return Number of entities that have the given components.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
|
||||
return *this ? handler->size() : size_type{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements that a group has currently
|
||||
* allocated space for.
|
||||
* @return Capacity of the group.
|
||||
*/
|
||||
[[nodiscard]] size_type capacity() const ENTT_NOEXCEPT {
|
||||
return *this ? handler->capacity() : size_type{};
|
||||
}
|
||||
|
||||
/*! @brief Requests the removal of unused capacity. */
|
||||
void shrink_to_fit() {
|
||||
if(*this) {
|
||||
handler->shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a group is empty.
|
||||
* @return True if the group is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return !*this || handler->empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Direct access to the list of entities.
|
||||
*
|
||||
* The returned pointer is such that range `[data(), data() + size())` is
|
||||
* always a valid range, even if the container is empty.
|
||||
*
|
||||
* @return A pointer to the array of entities.
|
||||
*/
|
||||
[[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT {
|
||||
return *this ? handler->data() : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the group.
|
||||
*
|
||||
* The returned iterator points to the first entity of the group. If the
|
||||
* group is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the group.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
|
||||
return *this ? handler->begin() : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the group.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the group. Attempting to dereference the returned iterator results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* group.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
|
||||
return *this ? handler->end() : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the reversed group.
|
||||
*
|
||||
* The returned iterator points to the first entity of the reversed group.
|
||||
* If the group is empty, the returned iterator will be equal to `rend()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the reversed group.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
|
||||
return *this ? handler->rbegin() : reverse_iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the reversed
|
||||
* group.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the reversed group. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* reversed group.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
|
||||
return *this ? handler->rend() : reverse_iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the first entity of the group, if any.
|
||||
* @return The first entity of the group if one exists, the null entity
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] entity_type front() const {
|
||||
const auto it = begin();
|
||||
return it != end() ? *it : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the last entity of the group, if any.
|
||||
* @return The last entity of the group if one exists, the null entity
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] entity_type back() const {
|
||||
const auto it = rbegin();
|
||||
return it != rend() ? *it : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds an entity.
|
||||
* @param entt A valid entity identifier.
|
||||
* @return An iterator to the given entity if it's found, past the end
|
||||
* iterator otherwise.
|
||||
*/
|
||||
[[nodiscard]] iterator find(const entity_type entt) const {
|
||||
const auto it = *this ? handler->find(entt) : iterator{};
|
||||
return it != end() && *it == entt ? it : end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the identifier that occupies the given position.
|
||||
* @param pos Position of the element to return.
|
||||
* @return The identifier that occupies the given position.
|
||||
*/
|
||||
[[nodiscard]] entity_type operator[](const size_type pos) const {
|
||||
return begin()[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a group is properly initialized.
|
||||
* @return True if the group is properly initialized, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return handler != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a group contains an entity.
|
||||
* @param entt A valid entity identifier.
|
||||
* @return True if the group contains the given entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(const entity_type entt) const {
|
||||
return *this && handler->contains(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
*
|
||||
* Prefer this function instead of `registry::get` during iterations. It has
|
||||
* far better performance than its counterpart.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an invalid component type results in a compilation
|
||||
* error. Attempting to use an entity that doesn't belong to the group
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @tparam Component Types of components to get.
|
||||
* @param entt A valid entity identifier.
|
||||
* @return The components assigned to the entity.
|
||||
*/
|
||||
template<typename... Component>
|
||||
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
|
||||
ENTT_ASSERT(contains(entt));
|
||||
|
||||
if constexpr(sizeof...(Component) == 0) {
|
||||
return std::tuple_cat(get_as_tuple(*std::get<storage_type<Get> *>(pools), entt)...);
|
||||
} else if constexpr(sizeof...(Component) == 1) {
|
||||
return (std::get<storage_type<Component> *>(pools)->get(entt), ...);
|
||||
} else {
|
||||
return std::tuple_cat(get_as_tuple(*std::get<storage_type<Component> *>(pools), entt)...);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and components and applies the given function
|
||||
* object to them.
|
||||
*
|
||||
* The function object is invoked for each entity. It is provided with the
|
||||
* entity itself and a set of references to non-empty components. The
|
||||
* _constness_ of the components is as requested.<br/>
|
||||
* The signature of the function must be equivalent to one of the following
|
||||
* forms:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type, Type &...);
|
||||
* void(Type &...);
|
||||
* @endcode
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated and therefore they are never
|
||||
* returned during iterations.
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
for(const auto entt: *this) {
|
||||
if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) {
|
||||
std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt)));
|
||||
} else {
|
||||
std::apply(func, get(entt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to use to _visit_ the group.
|
||||
*
|
||||
* The iterable object returns tuples that contain the current entity and a
|
||||
* set of references to its non-empty components. The _constness_ of the
|
||||
* components is as requested.
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated and therefore they are never
|
||||
* returned during iterations.
|
||||
*
|
||||
* @return An iterable object to use to _visit_ the group.
|
||||
*/
|
||||
[[nodiscard]] iterable_group each() const ENTT_NOEXCEPT {
|
||||
return iterable_group{handler, pools};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort a group according to the given comparison function.
|
||||
*
|
||||
* Sort the group so that iterating it with a couple of iterators returns
|
||||
* entities and components in the expected order. See `begin` and `end` for
|
||||
* more details.
|
||||
*
|
||||
* The comparison function object must return `true` if the first element
|
||||
* is _less_ than the second one, `false` otherwise. The signature of the
|
||||
* comparison function should be equivalent to one of the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* bool(std::tuple<Component &...>, std::tuple<Component &...>);
|
||||
* bool(const Component &..., const Component &...);
|
||||
* bool(const Entity, const Entity);
|
||||
* @endcode
|
||||
*
|
||||
* Where `Component` are such that they are iterated by the group.<br/>
|
||||
* Moreover, the comparison function object shall induce a
|
||||
* _strict weak ordering_ on the values.
|
||||
*
|
||||
* The sort function oject must offer a member function template
|
||||
* `operator()` that accepts three arguments:
|
||||
*
|
||||
* * An iterator to the first element of the range to sort.
|
||||
* * An iterator past the last element of the range to sort.
|
||||
* * A comparison function to use to compare the elements.
|
||||
*
|
||||
* @tparam Component Optional types of components to compare.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Sort Type of sort function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function object.
|
||||
* @param compare A valid comparison function object.
|
||||
* @param algo A valid sort function object.
|
||||
* @param args Arguments to forward to the sort function object, if any.
|
||||
*/
|
||||
template<typename... Component, typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(Compare compare, Sort algo = Sort{}, Args &&... args) {
|
||||
if(*this) {
|
||||
if constexpr(sizeof...(Component) == 0) {
|
||||
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
|
||||
handler->sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
} else if constexpr(sizeof...(Component) == 1) {
|
||||
handler->sort([this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) {
|
||||
return compare((std::get<storage_type<Component> *>(pools)->get(lhs), ...), (std::get<storage_type<Component> *>(pools)->get(rhs), ...));
|
||||
}, std::move(algo), std::forward<Args>(args)...);
|
||||
} else {
|
||||
handler->sort([this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) {
|
||||
return compare(std::forward_as_tuple(std::get<storage_type<Component> *>(pools)->get(lhs)...), std::forward_as_tuple(std::get<storage_type<Component> *>(pools)->get(rhs)...));
|
||||
}, std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort the shared pool of entities according to the given component.
|
||||
*
|
||||
* Non-owning groups of the same type share with the registry a pool of
|
||||
* entities with its own order that doesn't depend on the order of any pool
|
||||
* of components. Users can order the underlying data structure so that it
|
||||
* respects the order of the pool of the given component.
|
||||
*
|
||||
* @note
|
||||
* The shared pool of entities and thus its order is affected by the changes
|
||||
* to each and every pool that it tracks. Therefore changes to those pools
|
||||
* can quickly ruin the order imposed to the pool of entities shared between
|
||||
* the non-owning groups.
|
||||
*
|
||||
* @tparam Component Type of component to use to impose the order.
|
||||
*/
|
||||
template<typename Component>
|
||||
void sort() const {
|
||||
if(*this) {
|
||||
handler->respect(*std::get<storage_type<Component> *>(pools));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
basic_sparse_set<entity_type> * const handler;
|
||||
const std::tuple<storage_type<Get> *...> pools;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Owning group.
|
||||
*
|
||||
* Owning groups return all entities and only the entities that have at least
|
||||
* the given components. Moreover:
|
||||
*
|
||||
* * It's guaranteed that the entity list is tightly packed in memory for fast
|
||||
* iterations.
|
||||
* * It's guaranteed that the lists of owned components are tightly packed in
|
||||
* memory for even faster iterations and to allow direct access.
|
||||
* * They stay true to the order of the owned components and all instances have
|
||||
* the same order in memory.
|
||||
*
|
||||
* The more types of components are owned by a group, the faster it is to
|
||||
* iterate them.
|
||||
*
|
||||
* @b Important
|
||||
*
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all other cases, modifying the pools iterated by the group in any way
|
||||
* invalidates all the iterators and using them results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Groups share references to the underlying data structures of the registry
|
||||
* that generated them. Therefore any change to the entities and to the
|
||||
* components made by means of the registry are immediately reflected by all the
|
||||
* groups.
|
||||
* Moreover, sorting an owning group affects all the instance of the same group
|
||||
* (it means that users don't have to call `sort` on each instance to sort all
|
||||
* of them because they share the underlying data structure).
|
||||
*
|
||||
* @warning
|
||||
* Lifetime of a group must not overcome that of the registry that generated it.
|
||||
* In any other case, attempting to use a group results in undefined behavior.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Exclude Types of components used to filter the group.
|
||||
* @tparam Get Types of components observed by the group.
|
||||
* @tparam Owned Types of components owned by the group.
|
||||
*/
|
||||
template<typename Entity, typename... Exclude, typename... Get, typename... Owned>
|
||||
class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned...> final {
|
||||
/*! @brief A registry is allowed to create groups. */
|
||||
friend class basic_registry<Entity>;
|
||||
|
||||
template<typename Component>
|
||||
using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Component>>::storage_type, Component>;
|
||||
|
||||
class iterable_group final {
|
||||
friend class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned...>;
|
||||
|
||||
template<typename, typename>
|
||||
class iterable_group_iterator;
|
||||
|
||||
template<typename It, typename... OIt>
|
||||
class iterable_group_iterator<It, type_list<OIt...>> final {
|
||||
friend class iterable_group;
|
||||
|
||||
template<typename... Other>
|
||||
iterable_group_iterator(It from, const std::tuple<Other...> &other, const std::tuple<storage_type<Get> *...> &cpools) ENTT_NOEXCEPT
|
||||
: it{from},
|
||||
owned{std::get<OIt>(other)...},
|
||||
get{cpools}
|
||||
{}
|
||||
|
||||
public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
|
||||
using pointer = void;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
iterable_group_iterator & operator++() ENTT_NOEXCEPT {
|
||||
return ++it, (++std::get<OIt>(owned), ...), *this;
|
||||
}
|
||||
|
||||
iterable_group_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
iterable_group_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
|
||||
return std::tuple_cat(
|
||||
std::make_tuple(*it),
|
||||
std::forward_as_tuple(*std::get<OIt>(owned)...),
|
||||
get_as_tuple(*std::get<storage_type<Get> *>(get), *it)...
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const iterable_group_iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.it == it;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator!=(const iterable_group_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
It it;
|
||||
std::tuple<OIt...> owned;
|
||||
std::tuple<storage_type<Get> *...> get;
|
||||
};
|
||||
|
||||
iterable_group(std::tuple<storage_type<Owned> *..., storage_type<Get> *...> cpools, const std::size_t * const extent)
|
||||
: pools{cpools},
|
||||
length{extent}
|
||||
{}
|
||||
|
||||
public:
|
||||
using iterator = iterable_group_iterator<
|
||||
typename basic_sparse_set<Entity>::iterator,
|
||||
type_list_cat_t<std::conditional_t<std::is_void_v<decltype(std::declval<storage_type<Owned>>().get({}))>, type_list<>, type_list<decltype(std::declval<storage_type<Owned>>().end())>>...>
|
||||
>;
|
||||
using reverse_iterator = iterable_group_iterator<
|
||||
typename basic_sparse_set<Entity>::reverse_iterator,
|
||||
type_list_cat_t<std::conditional_t<std::is_void_v<decltype(std::declval<storage_type<Owned>>().get({}))>, type_list<>, type_list<decltype(std::declval<storage_type<Owned>>().rbegin())>>...>
|
||||
>;
|
||||
|
||||
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
|
||||
return length ? iterator{
|
||||
std::get<0>(pools)->basic_sparse_set<Entity>::end() - *length,
|
||||
std::make_tuple((std::get<storage_type<Owned> *>(pools)->end() - *length)...),
|
||||
std::make_tuple(std::get<storage_type<Get> *>(pools)...)
|
||||
} : iterator{{}, std::make_tuple(decltype(std::get<storage_type<Owned> *>(pools)->end()){}...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
|
||||
}
|
||||
|
||||
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
|
||||
return length ? iterator{
|
||||
std::get<0>(pools)->basic_sparse_set<Entity>::end(),
|
||||
std::make_tuple((std::get<storage_type<Owned> *>(pools)->end())...),
|
||||
std::make_tuple(std::get<storage_type<Get> *>(pools)...)
|
||||
} : iterator{{}, std::make_tuple(decltype(std::get<storage_type<Owned> *>(pools)->end()){}...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
|
||||
}
|
||||
|
||||
[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
|
||||
return length ? reverse_iterator{
|
||||
std::get<0>(pools)->basic_sparse_set<Entity>::rbegin(),
|
||||
std::make_tuple((std::get<storage_type<Owned> *>(pools)->rbegin())...),
|
||||
std::make_tuple(std::get<storage_type<Get> *>(pools)...)
|
||||
} : reverse_iterator{{}, std::make_tuple(decltype(std::get<storage_type<Owned> *>(pools)->rbegin()){}...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
|
||||
}
|
||||
|
||||
[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
|
||||
return length ? reverse_iterator{
|
||||
std::get<0>(pools)->basic_sparse_set<Entity>::rbegin() + *length,
|
||||
std::make_tuple((std::get<storage_type<Owned> *>(pools)->rbegin() + *length)...),
|
||||
std::make_tuple(std::get<storage_type<Get> *>(pools)...)
|
||||
} : reverse_iterator{{}, std::make_tuple(decltype(std::get<storage_type<Owned> *>(pools)->rbegin()){}...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
|
||||
}
|
||||
|
||||
private:
|
||||
const std::tuple<storage_type<Owned> *..., storage_type<Get> *...> pools;
|
||||
const std::size_t * const length;
|
||||
};
|
||||
|
||||
basic_group(const std::size_t &extent, storage_type<Owned> &... opool, storage_type<Get> &... gpool) ENTT_NOEXCEPT
|
||||
: pools{&opool..., &gpool...},
|
||||
length{&extent}
|
||||
{}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = typename basic_sparse_set<Entity>::iterator;
|
||||
/*! @brief Reversed iterator type. */
|
||||
using reverse_iterator = typename basic_sparse_set<Entity>::reverse_iterator;
|
||||
|
||||
/*! @brief Default constructor to use to create empty, invalid groups. */
|
||||
basic_group() ENTT_NOEXCEPT
|
||||
: length{}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of entities that have the given components.
|
||||
* @return Number of entities that have the given components.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
|
||||
return *this ? *length : size_type{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a group is empty.
|
||||
* @return True if the group is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return !*this || !*length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Direct access to the list of components of a given pool.
|
||||
*
|
||||
* The returned pointer is such that range
|
||||
* `[raw<Component>(), raw<Component>() + size())` is always a valid range,
|
||||
* even if the container is empty.<br/>
|
||||
*
|
||||
* @warning
|
||||
* This function is only available for owned types.
|
||||
*
|
||||
* @tparam Component Type of component in which one is interested.
|
||||
* @return A pointer to the array of components.
|
||||
*/
|
||||
template<typename Component>
|
||||
[[nodiscard]] Component * raw() const ENTT_NOEXCEPT {
|
||||
static_assert((std::is_same_v<Component, Owned> || ...), "Non-owned type");
|
||||
auto *cpool = std::get<storage_type<Component> *>(pools);
|
||||
return cpool ? cpool->raw() : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Direct access to the list of entities.
|
||||
*
|
||||
* The returned pointer is such that range `[data(), data() + size())` is
|
||||
* always a valid range, even if the container is empty.
|
||||
*
|
||||
* @return A pointer to the array of entities.
|
||||
*/
|
||||
[[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT {
|
||||
return *this ? std::get<0>(pools)->data() : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the group.
|
||||
*
|
||||
* The returned iterator points to the first entity of the group. If the
|
||||
* group is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the group.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
|
||||
return *this ? (std::get<0>(pools)->basic_sparse_set<entity_type>::end() - *length) : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the group.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the group. Attempting to dereference the returned iterator results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* group.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
|
||||
return *this ? std::get<0>(pools)->basic_sparse_set<entity_type>::end() : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the reversed group.
|
||||
*
|
||||
* The returned iterator points to the first entity of the reversed group.
|
||||
* If the group is empty, the returned iterator will be equal to `rend()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the reversed group.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
|
||||
return *this ? std::get<0>(pools)->basic_sparse_set<entity_type>::rbegin() : reverse_iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the reversed
|
||||
* group.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the reversed group. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* reversed group.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
|
||||
return *this ? (std::get<0>(pools)->basic_sparse_set<entity_type>::rbegin() + *length) : reverse_iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the first entity of the group, if any.
|
||||
* @return The first entity of the group if one exists, the null entity
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] entity_type front() const {
|
||||
const auto it = begin();
|
||||
return it != end() ? *it : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the last entity of the group, if any.
|
||||
* @return The last entity of the group if one exists, the null entity
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] entity_type back() const {
|
||||
const auto it = rbegin();
|
||||
return it != rend() ? *it : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds an entity.
|
||||
* @param entt A valid entity identifier.
|
||||
* @return An iterator to the given entity if it's found, past the end
|
||||
* iterator otherwise.
|
||||
*/
|
||||
[[nodiscard]] iterator find(const entity_type entt) const {
|
||||
const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{};
|
||||
return it != end() && it >= begin() && *it == entt ? it : end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the identifier that occupies the given position.
|
||||
* @param pos Position of the element to return.
|
||||
* @return The identifier that occupies the given position.
|
||||
*/
|
||||
[[nodiscard]] entity_type operator[](const size_type pos) const {
|
||||
return begin()[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a group is properly initialized.
|
||||
* @return True if the group is properly initialized, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return length != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a group contains an entity.
|
||||
* @param entt A valid entity identifier.
|
||||
* @return True if the group contains the given entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(const entity_type entt) const {
|
||||
return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
*
|
||||
* Prefer this function instead of `registry::get` during iterations. It has
|
||||
* far better performance than its counterpart.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an invalid component type results in a compilation
|
||||
* error. Attempting to use an entity that doesn't belong to the group
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @tparam Component Types of components to get.
|
||||
* @param entt A valid entity identifier.
|
||||
* @return The components assigned to the entity.
|
||||
*/
|
||||
template<typename... Component>
|
||||
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
|
||||
ENTT_ASSERT(contains(entt));
|
||||
|
||||
if constexpr(sizeof...(Component) == 0) {
|
||||
return std::tuple_cat(get_as_tuple(*std::get<storage_type<Owned> *>(pools), entt)..., get_as_tuple(*std::get<storage_type<Get> *>(pools), entt)...);
|
||||
} else if constexpr(sizeof...(Component) == 1) {
|
||||
return (std::get<storage_type<Component> *>(pools)->get(entt), ...);
|
||||
} else {
|
||||
return std::tuple_cat(get_as_tuple(*std::get<storage_type<Component> *>(pools), entt)...);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and components and applies the given function
|
||||
* object to them.
|
||||
*
|
||||
* The function object is invoked for each entity. It is provided with the
|
||||
* entity itself and a set of references to non-empty components. The
|
||||
* _constness_ of the components is as requested.<br/>
|
||||
* The signature of the function must be equivalent to one of the following
|
||||
* forms:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type, Type &...);
|
||||
* void(Type &...);
|
||||
* @endcode
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated and therefore they are never
|
||||
* returned during iterations.
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
for(auto args: each()) {
|
||||
if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) {
|
||||
std::apply(func, args);
|
||||
} else {
|
||||
std::apply([&func](auto, auto &&... less) { func(std::forward<decltype(less)>(less)...); }, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to use to _visit_ the group.
|
||||
*
|
||||
* The iterable object returns tuples that contain the current entity and a
|
||||
* set of references to its non-empty components. The _constness_ of the
|
||||
* components is as requested.
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated and therefore they are never
|
||||
* returned during iterations.
|
||||
*
|
||||
* @return An iterable object to use to _visit_ the group.
|
||||
*/
|
||||
[[nodiscard]] iterable_group each() const ENTT_NOEXCEPT {
|
||||
return iterable_group{pools, length};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort a group according to the given comparison function.
|
||||
*
|
||||
* Sort the group so that iterating it with a couple of iterators returns
|
||||
* entities and components in the expected order. See `begin` and `end` for
|
||||
* more details.
|
||||
*
|
||||
* The comparison function object must return `true` if the first element
|
||||
* is _less_ than the second one, `false` otherwise. The signature of the
|
||||
* comparison function should be equivalent to one of the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* bool(std::tuple<Component &...>, std::tuple<Component &...>);
|
||||
* bool(const Component &, const Component &);
|
||||
* bool(const Entity, const Entity);
|
||||
* @endcode
|
||||
*
|
||||
* Where `Component` are either owned types or not but still such that they
|
||||
* are iterated by the group.<br/>
|
||||
* Moreover, the comparison function object shall induce a
|
||||
* _strict weak ordering_ on the values.
|
||||
*
|
||||
* The sort function oject must offer a member function template
|
||||
* `operator()` that accepts three arguments:
|
||||
*
|
||||
* * An iterator to the first element of the range to sort.
|
||||
* * An iterator past the last element of the range to sort.
|
||||
* * A comparison function to use to compare the elements.
|
||||
*
|
||||
* @tparam Component Optional types of components to compare.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Sort Type of sort function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function object.
|
||||
* @param compare A valid comparison function object.
|
||||
* @param algo A valid sort function object.
|
||||
* @param args Arguments to forward to the sort function object, if any.
|
||||
*/
|
||||
template<typename... Component, typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(Compare compare, Sort algo = Sort{}, Args &&... args) const {
|
||||
auto *cpool = std::get<0>(pools);
|
||||
|
||||
if constexpr(sizeof...(Component) == 0) {
|
||||
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
|
||||
cpool->sort_n(*length, std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
} else if constexpr(sizeof...(Component) == 1) {
|
||||
cpool->sort_n(*length, [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) {
|
||||
return compare((std::get<storage_type<Component> *>(pools)->get(lhs), ...), (std::get<storage_type<Component> *>(pools)->get(rhs), ...));
|
||||
}, std::move(algo), std::forward<Args>(args)...);
|
||||
} else {
|
||||
cpool->sort_n(*length, [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) {
|
||||
return compare(std::forward_as_tuple(std::get<storage_type<Component> *>(pools)->get(lhs)...), std::forward_as_tuple(std::get<storage_type<Component> *>(pools)->get(rhs)...));
|
||||
}, std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
[this](auto *head, auto *... other) {
|
||||
for(auto next = *length; next; --next) {
|
||||
const auto pos = next - 1;
|
||||
[[maybe_unused]] const auto entt = head->data()[pos];
|
||||
(other->swap(other->data()[pos], entt), ...);
|
||||
}
|
||||
}(std::get<storage_type<Owned> *>(pools)...);
|
||||
}
|
||||
|
||||
private:
|
||||
const std::tuple<storage_type<Owned> *..., storage_type<Get> *...> pools;
|
||||
const size_type * const length;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
341
src/entt/entity/handle.hpp
Normal file
341
src/entt/entity/handle.hpp
Normal file
@@ -0,0 +1,341 @@
|
||||
#ifndef ENTT_ENTITY_HANDLE_HPP
|
||||
#define ENTT_ENTITY_HANDLE_HPP
|
||||
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "registry.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Non-owning handle to an entity.
|
||||
*
|
||||
* Tiny wrapper around a registry and an entity.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Type Types to which to restrict the scope of a handle.
|
||||
*/
|
||||
template<typename Entity, typename... Type>
|
||||
struct basic_handle {
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = std::remove_const_t<Entity>;
|
||||
/*! @brief Type of registry accepted by the handle. */
|
||||
using registry_type = constness_as_t<basic_registry<entity_type>, Entity>;
|
||||
|
||||
/*! @brief Constructs an invalid handle. */
|
||||
basic_handle() ENTT_NOEXCEPT
|
||||
: reg{}, entt{null}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Constructs a handle from a given registry and entity.
|
||||
* @param ref An instance of the registry class.
|
||||
* @param value An entity identifier.
|
||||
*/
|
||||
basic_handle(registry_type &ref, entity_type value) ENTT_NOEXCEPT
|
||||
: reg{&ref}, entt{value}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Compares two handles.
|
||||
* @tparam Args Template parameters of the handle with which to compare.
|
||||
* @param other Handle with which to compare.
|
||||
* @return True if both handles refer to the same registry and the same
|
||||
* entity, false otherwise.
|
||||
*/
|
||||
template<typename... Args>
|
||||
[[nodiscard]] bool operator==(const basic_handle<Args...> &other) const ENTT_NOEXCEPT {
|
||||
return reg == other.registry() && entt == other.entity();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs a const handle from a non-const one.
|
||||
* @tparam Other A valid entity type (see entt_traits for more details).
|
||||
* @tparam Args Scope of the handle to construct.
|
||||
* @return A const handle referring to the same registry and the same
|
||||
* entity.
|
||||
*/
|
||||
template<typename Other, typename... Args>
|
||||
operator basic_handle<Other, Args...>() const ENTT_NOEXCEPT {
|
||||
static_assert(
|
||||
(std::is_same_v<Other, Entity> || std::is_same_v<std::remove_const_t<Other>, Entity>)
|
||||
&& (sizeof...(Type) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Type)) && ... && (type_list_contains_v<type_list<Type...>, Args>))),
|
||||
"Invalid conversion between different handles"
|
||||
);
|
||||
|
||||
return reg ? basic_handle<Other, Args...>{*reg, entt} : basic_handle<Other, Args...>{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts a handle to its underlying entity.
|
||||
* @return An entity identifier.
|
||||
*/
|
||||
[[nodiscard]] operator entity_type() const ENTT_NOEXCEPT {
|
||||
return entity();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle refers to non-null registry pointer and entity.
|
||||
* @return True if the handle refers to non-null registry and entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return reg && reg->valid(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle refers to a valid entity or not.
|
||||
* @return True if the handle refers to a valid entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool valid() const {
|
||||
return reg->valid(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to the underlying registry, if any.
|
||||
* @return A pointer to the underlying registry, if any.
|
||||
*/
|
||||
[[nodiscard]] registry_type * registry() const ENTT_NOEXCEPT {
|
||||
return reg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the entity associated with a handle.
|
||||
* @return The entity associated with the handle.
|
||||
*/
|
||||
[[nodiscard]] entity_type entity() const ENTT_NOEXCEPT {
|
||||
return entt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys the entity associated with a handle.
|
||||
* @sa basic_registry::destroy
|
||||
*/
|
||||
void destroy() {
|
||||
reg->destroy(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys the entity associated with a handle.
|
||||
* @sa basic_registry::destroy
|
||||
* @param version A desired version upon destruction.
|
||||
*/
|
||||
void destroy(const typename registry_type::version_type version) {
|
||||
reg->destroy(entt, version);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns the given component to a handle.
|
||||
* @sa basic_registry::emplace
|
||||
* @tparam Component Type of component to create.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
* @return A reference to the newly created component.
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
decltype(auto) emplace(Args &&... args) const {
|
||||
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
|
||||
return reg->template emplace<Component>(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns or replaces the given component for a handle.
|
||||
* @sa basic_registry::emplace_or_replace
|
||||
* @tparam Component Type of component to assign or replace.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
* @return A reference to the newly created component.
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
decltype(auto) emplace_or_replace(Args &&... args) const {
|
||||
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
|
||||
return reg->template emplace_or_replace<Component>(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Patches the given component for a handle.
|
||||
* @sa basic_registry::patch
|
||||
* @tparam Component Type of component to patch.
|
||||
* @tparam Func Types of the function objects to invoke.
|
||||
* @param func Valid function objects.
|
||||
* @return A reference to the patched component.
|
||||
*/
|
||||
template<typename Component, typename... Func>
|
||||
decltype(auto) patch(Func &&... func) const {
|
||||
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
|
||||
return reg->template patch<Component>(entt, std::forward<Func>(func)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Replaces the given component for a handle.
|
||||
* @sa basic_registry::replace
|
||||
* @tparam Component Type of component to replace.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
* @return A reference to the component being replaced.
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
decltype(auto) replace(Args &&... args) const {
|
||||
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
|
||||
return reg->template replace<Component>(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the given components from a handle.
|
||||
* @sa basic_registry::remove
|
||||
* @tparam Component Types of components to remove.
|
||||
*/
|
||||
template<typename... Component>
|
||||
void remove() const {
|
||||
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
|
||||
reg->template remove<Component...>(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... Component>
|
||||
decltype(auto) remove_if_exists() const {
|
||||
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
|
||||
return reg->template remove_if_exists<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes all the components from a handle and makes it orphaned.
|
||||
* @sa basic_registry::remove_all
|
||||
*/
|
||||
void remove_all() const {
|
||||
static_assert(sizeof...(Type) == 0, "Invalid operation");
|
||||
reg->remove_all(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle has all the given components.
|
||||
* @sa basic_registry::all_of
|
||||
* @tparam Component Components for which to perform the check.
|
||||
* @return True if the handle has all the components, false otherwise.
|
||||
*/
|
||||
template<typename... Component>
|
||||
[[nodiscard]] decltype(auto) all_of() const {
|
||||
return reg->template all_of<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle has at least one of the given components.
|
||||
* @sa basic_registry::any_of
|
||||
* @tparam Component Components for which to perform the check.
|
||||
* @return True if the handle has at least one of the given components,
|
||||
* false otherwise.
|
||||
*/
|
||||
template<typename... Component>
|
||||
[[nodiscard]] decltype(auto) any_of() const {
|
||||
return reg->template any_of<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns references to the given components for a handle.
|
||||
* @sa basic_registry::get
|
||||
* @tparam Component Types of components to get.
|
||||
* @return References to the components owned by the handle.
|
||||
*/
|
||||
template<typename... Component>
|
||||
[[nodiscard]] decltype(auto) get() const {
|
||||
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
|
||||
return reg->template get<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the given component for a handle.
|
||||
* @sa basic_registry::get_or_emplace
|
||||
* @tparam Component Type of component to get.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
* @return Reference to the component owned by the handle.
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
[[nodiscard]] decltype(auto) get_or_emplace(Args &&... args) const {
|
||||
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
|
||||
return reg->template get_or_emplace<Component>(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns pointers to the given components for a handle.
|
||||
* @sa basic_registry::try_get
|
||||
* @tparam Component Types of components to get.
|
||||
* @return Pointers to the components owned by the handle.
|
||||
*/
|
||||
template<typename... Component>
|
||||
[[nodiscard]] auto try_get() const {
|
||||
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
|
||||
return reg->template try_get<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle has components assigned.
|
||||
* @return True if the handle has no components assigned, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool orphan() const {
|
||||
return reg->orphan(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Visits a handle and returns the 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 Compares two handles.
|
||||
* @tparam Type A valid entity type (see entt_traits for more details).
|
||||
* @tparam Other A valid entity type (see entt_traits for more details).
|
||||
* @param lhs A valid handle.
|
||||
* @param rhs A valid handle.
|
||||
* @return False if both handles refer to the same registry and the same
|
||||
* entity, true otherwise.
|
||||
*/
|
||||
template<typename Type, typename Other>
|
||||
bool operator!=(const basic_handle<Type> &lhs, const basic_handle<Other> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
basic_handle(basic_registry<Entity> &, Entity) -> basic_handle<Entity>;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
basic_handle(const basic_registry<Entity> &, Entity) -> basic_handle<const Entity>;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
156
src/entt/entity/helper.hpp
Normal file
156
src/entt/entity/helper.hpp
Normal file
@@ -0,0 +1,156 @@
|
||||
#ifndef ENTT_ENTITY_HELPER_HPP
|
||||
#define ENTT_ENTITY_HELPER_HPP
|
||||
|
||||
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../signal/delegate.hpp"
|
||||
#include "registry.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Converts a registry to a view.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
struct as_view {
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = std::remove_const_t<Entity>;
|
||||
/*! @brief Type of registry to convert. */
|
||||
using registry_type = constness_as_t<basic_registry<entity_type>, Entity>;
|
||||
|
||||
/**
|
||||
* @brief Constructs a converter for a given registry.
|
||||
* @param source A valid reference to a registry.
|
||||
*/
|
||||
as_view(registry_type &source) ENTT_NOEXCEPT: reg{source} {}
|
||||
|
||||
/**
|
||||
* @brief Conversion function from a registry to a view.
|
||||
* @tparam Exclude Types of components used to filter the view.
|
||||
* @tparam Component Type of components used to construct the view.
|
||||
* @return A newly created view.
|
||||
*/
|
||||
template<typename Exclude, typename... Component>
|
||||
operator basic_view<entity_type, Exclude, Component...>() const {
|
||||
return reg.template view<Component...>(Exclude{});
|
||||
}
|
||||
|
||||
private:
|
||||
registry_type ®
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
*
|
||||
* It allows to deduce the constness of a registry directly from the instance
|
||||
* provided to the constructor.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
as_view(basic_registry<Entity> &) ENTT_NOEXCEPT -> as_view<Entity>;
|
||||
|
||||
|
||||
/*! @copydoc as_view */
|
||||
template<typename Entity>
|
||||
as_view(const basic_registry<Entity> &) ENTT_NOEXCEPT -> as_view<const Entity>;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Converts a registry to a group.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
struct as_group {
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = std::remove_const_t<Entity>;
|
||||
/*! @brief Type of registry to convert. */
|
||||
using registry_type = constness_as_t<basic_registry<entity_type>, Entity>;
|
||||
|
||||
/**
|
||||
* @brief Constructs a converter for a given registry.
|
||||
* @param source A valid reference to a registry.
|
||||
*/
|
||||
as_group(registry_type &source) ENTT_NOEXCEPT: reg{source} {}
|
||||
|
||||
/**
|
||||
* @brief Conversion function from a registry to a group.
|
||||
* @tparam Exclude Types of components used to filter the group.
|
||||
* @tparam Get Types of components observed by the group.
|
||||
* @tparam Owned Types of components owned by the group.
|
||||
* @return A newly created group.
|
||||
*/
|
||||
template<typename Exclude, typename Get, typename... Owned>
|
||||
operator basic_group<entity_type, Exclude, Get, Owned...>() const {
|
||||
if constexpr(std::is_const_v<registry_type>) {
|
||||
return reg.template group_if_exists<Owned...>(Get{}, Exclude{});
|
||||
} else {
|
||||
return reg.template group<Owned...>(Get{}, Exclude{});
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
registry_type ®
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
*
|
||||
* It allows to deduce the constness of a registry directly from the instance
|
||||
* provided to the constructor.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
as_group(basic_registry<Entity> &) ENTT_NOEXCEPT -> as_group<Entity>;
|
||||
|
||||
|
||||
/*! @copydoc as_group */
|
||||
template<typename Entity>
|
||||
as_group(const basic_registry<Entity> &) ENTT_NOEXCEPT -> as_group<const Entity>;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper to create a listener that directly invokes a member function.
|
||||
* @tparam Member Member function to invoke on a component of the given type.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @param reg A registry that contains the given entity and its components.
|
||||
* @param entt Entity from which to get the component.
|
||||
*/
|
||||
template<auto Member, typename Entity = entity>
|
||||
void invoke(basic_registry<Entity> ®, const Entity entt) {
|
||||
static_assert(std::is_member_function_pointer_v<decltype(Member)>, "Invalid pointer to non-static member function");
|
||||
delegate<void(basic_registry<Entity> &, const Entity)> func;
|
||||
func.template connect<Member>(reg.template get<member_class_t<decltype(Member)>>(entt));
|
||||
func(reg, entt);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns the entity associated with a given component.
|
||||
* @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) {
|
||||
const auto view = reg.template view<const Component>();
|
||||
return *(view.data() + (&component - view.raw()));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
446
src/entt/entity/observer.hpp
Normal file
446
src/entt/entity/observer.hpp
Normal file
@@ -0,0 +1,446 @@
|
||||
#ifndef ENTT_ENTITY_OBSERVER_HPP
|
||||
#define ENTT_ENTITY_OBSERVER_HPP
|
||||
|
||||
|
||||
#include <limits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../signal/delegate.hpp"
|
||||
#include "registry.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/*! @brief Grouping matcher. */
|
||||
template<typename...>
|
||||
struct matcher {};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Collector.
|
||||
*
|
||||
* Primary template isn't defined on purpose. All the specializations give a
|
||||
* compile-time error, but for a few reasonable cases.
|
||||
*/
|
||||
template<typename...>
|
||||
struct basic_collector;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Collector.
|
||||
*
|
||||
* A collector contains a set of rules (literally, matchers) to use to track
|
||||
* entities.<br/>
|
||||
* Its main purpose is to generate a descriptor that allows an observer to know
|
||||
* how to connect to a registry.
|
||||
*/
|
||||
template<>
|
||||
struct basic_collector<> {
|
||||
/**
|
||||
* @brief Adds a grouping matcher to the collector.
|
||||
* @tparam AllOf Types of components tracked by the matcher.
|
||||
* @tparam NoneOf Types of components used to filter out entities.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>>{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds an observing matcher to the collector.
|
||||
* @tparam AnyOf Type of component for which changes should be detected.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename AnyOf>
|
||||
static constexpr auto update() ENTT_NOEXCEPT {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, AnyOf>>{};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Collector.
|
||||
* @copydetails basic_collector<>
|
||||
* @tparam Reject Untracked types used to filter out entities.
|
||||
* @tparam Require Untracked types required by the matcher.
|
||||
* @tparam Rule Specific details of the current matcher.
|
||||
* @tparam Other Other matchers.
|
||||
*/
|
||||
template<typename... Reject, typename... Require, typename... Rule, typename... Other>
|
||||
struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule...>, Other...> {
|
||||
/*! @brief Current matcher. */
|
||||
using current_type = matcher<type_list<Reject...>, type_list<Require...>, Rule...>;
|
||||
|
||||
/**
|
||||
* @brief Adds a grouping matcher to the collector.
|
||||
* @tparam AllOf Types of components tracked by the matcher.
|
||||
* @tparam NoneOf Types of components used to filter out entities.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>, current_type, Other...>{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds an observing matcher to the collector.
|
||||
* @tparam AnyOf Type of component for which changes should be detected.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename AnyOf>
|
||||
static constexpr auto update() ENTT_NOEXCEPT {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, AnyOf>, current_type, Other...>{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates the filter of the last added matcher.
|
||||
* @tparam AllOf Types of components required by the matcher.
|
||||
* @tparam NoneOf Types of components used to filter out entities.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
static constexpr auto where(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
|
||||
using extended_type = matcher<type_list<Reject..., NoneOf...>, type_list<Require..., AllOf...>, Rule...>;
|
||||
return basic_collector<extended_type, Other...>{};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*! @brief Variable template used to ease the definition of collectors. */
|
||||
inline constexpr basic_collector<> collector{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Observer.
|
||||
*
|
||||
* An observer returns all the entities and only the entities that fit the
|
||||
* requirements of at least one matcher. Moreover, it's guaranteed that the
|
||||
* entity list is tightly packed in memory for fast iterations.<br/>
|
||||
* In general, observers don't stay true to the order of any set of components.
|
||||
*
|
||||
* Observers work mainly with two types of matchers, provided through a
|
||||
* collector:
|
||||
*
|
||||
* * Observing matcher: an observer will return at least all the living entities
|
||||
* for which one or more of the given components have been updated and not yet
|
||||
* destroyed.
|
||||
* * Grouping matcher: an observer will return at least all the living entities
|
||||
* that would have entered the given group if it existed and that would have
|
||||
* not yet left it.
|
||||
*
|
||||
* If an entity respects the requirements of multiple matchers, it will be
|
||||
* returned once and only once by the observer in any case.
|
||||
*
|
||||
* Matchers support also filtering by means of a _where_ clause that accepts
|
||||
* both a list of types and an exclusion list.<br/>
|
||||
* Whenever a matcher finds that an entity matches its requirements, the
|
||||
* condition of the filter is verified before to register the entity itself.
|
||||
* Moreover, a registered entity isn't returned by the observer if the condition
|
||||
* set by the filter is broken in the meantime.
|
||||
*
|
||||
* @b Important
|
||||
*
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all the other cases, modifying the pools of the given components in any
|
||||
* way invalidates all the iterators and using them results in undefined
|
||||
* behavior.
|
||||
*
|
||||
* @warning
|
||||
* Lifetime of an observer doesn't necessarily have to overcome that of the
|
||||
* registry to which it is connected. However, the observer must be disconnected
|
||||
* from the registry before being destroyed to avoid crashes due to dangling
|
||||
* pointers.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
class basic_observer {
|
||||
using payload_type = std::uint32_t;
|
||||
|
||||
template<typename>
|
||||
struct matcher_handler;
|
||||
|
||||
template<typename... Reject, typename... Require, typename AnyOf>
|
||||
struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, AnyOf>> {
|
||||
template<std::size_t Index>
|
||||
static void maybe_valid_if(basic_observer &obs, basic_registry<Entity> ®, const Entity entt) {
|
||||
if(reg.template all_of<Require...>(entt) && !reg.template any_of<Reject...>(entt)) {
|
||||
if(!obs.storage.contains(entt)) {
|
||||
obs.storage.emplace(entt);
|
||||
}
|
||||
|
||||
obs.storage.get(entt) |= (1 << Index);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void discard_if(basic_observer &obs, basic_registry<Entity> &, const Entity entt) {
|
||||
if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) {
|
||||
obs.storage.remove(entt);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void connect(basic_observer &obs, basic_registry<Entity> ®) {
|
||||
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
|
||||
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
|
||||
reg.template on_update<AnyOf>().template connect<&maybe_valid_if<Index>>(obs);
|
||||
reg.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(obs);
|
||||
}
|
||||
|
||||
static void disconnect(basic_observer &obs, basic_registry<Entity> ®) {
|
||||
(reg.template on_destroy<Require>().disconnect(obs), ...);
|
||||
(reg.template on_construct<Reject>().disconnect(obs), ...);
|
||||
reg.template on_update<AnyOf>().disconnect(obs);
|
||||
reg.template on_destroy<AnyOf>().disconnect(obs);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Reject, typename... Require, typename... NoneOf, typename... AllOf>
|
||||
struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, type_list<NoneOf...>, AllOf...>> {
|
||||
template<std::size_t Index, typename... Ignore>
|
||||
static void maybe_valid_if(basic_observer &obs, basic_registry<Entity> ®, const Entity entt) {
|
||||
if([®, entt]() {
|
||||
if constexpr(sizeof...(Ignore) == 0) {
|
||||
return reg.template all_of<AllOf..., Require...>(entt) && !reg.template any_of<NoneOf..., Reject...>(entt);
|
||||
} else {
|
||||
return reg.template all_of<AllOf..., Require...>(entt) && ((std::is_same_v<Ignore..., NoneOf> || !reg.template any_of<NoneOf>(entt)) && ...) && !reg.template any_of<Reject...>(entt);
|
||||
}
|
||||
}())
|
||||
{
|
||||
if(!obs.storage.contains(entt)) {
|
||||
obs.storage.emplace(entt);
|
||||
}
|
||||
|
||||
obs.storage.get(entt) |= (1 << Index);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void discard_if(basic_observer &obs, basic_registry<Entity> &, const Entity entt) {
|
||||
if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) {
|
||||
obs.storage.remove(entt);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void connect(basic_observer &obs, basic_registry<Entity> ®) {
|
||||
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
|
||||
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
|
||||
(reg.template on_construct<AllOf>().template connect<&maybe_valid_if<Index>>(obs), ...);
|
||||
(reg.template on_destroy<NoneOf>().template connect<&maybe_valid_if<Index, NoneOf>>(obs), ...);
|
||||
(reg.template on_destroy<AllOf>().template connect<&discard_if<Index>>(obs), ...);
|
||||
(reg.template on_construct<NoneOf>().template connect<&discard_if<Index>>(obs), ...);
|
||||
}
|
||||
|
||||
static void disconnect(basic_observer &obs, basic_registry<Entity> ®) {
|
||||
(reg.template on_destroy<Require>().disconnect(obs), ...);
|
||||
(reg.template on_construct<Reject>().disconnect(obs), ...);
|
||||
(reg.template on_construct<AllOf>().disconnect(obs), ...);
|
||||
(reg.template on_destroy<NoneOf>().disconnect(obs), ...);
|
||||
(reg.template on_destroy<AllOf>().disconnect(obs), ...);
|
||||
(reg.template on_construct<NoneOf>().disconnect(obs), ...);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Matcher>
|
||||
static void disconnect(basic_registry<Entity> ®, basic_observer &obs) {
|
||||
(matcher_handler<Matcher>::disconnect(obs, reg), ...);
|
||||
}
|
||||
|
||||
template<typename... Matcher, std::size_t... Index>
|
||||
void connect(basic_registry<Entity> ®, std::index_sequence<Index...>) {
|
||||
static_assert(sizeof...(Matcher) < std::numeric_limits<payload_type>::digits, "Too many matchers");
|
||||
(matcher_handler<Matcher>::template connect<Index>(*this, reg), ...);
|
||||
release.template connect<&basic_observer::disconnect<Matcher...>>(reg);
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = typename basic_sparse_set<Entity>::iterator;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_observer()
|
||||
: release{},
|
||||
storage{}
|
||||
{}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_observer(const basic_observer &) = delete;
|
||||
/*! @brief Default move constructor, deleted on purpose. */
|
||||
basic_observer(basic_observer &&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Creates an observer and connects it to a given registry.
|
||||
* @tparam Matcher Types of matchers to use to initialize the observer.
|
||||
* @param reg A valid reference to a registry.
|
||||
*/
|
||||
template<typename... Matcher>
|
||||
basic_observer(basic_registry<entity_type> ®, basic_collector<Matcher...>)
|
||||
: basic_observer{}
|
||||
{
|
||||
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
|
||||
}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_observer() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This observer.
|
||||
*/
|
||||
basic_observer & operator=(const basic_observer &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator, deleted on purpose.
|
||||
* @return This observer.
|
||||
*/
|
||||
basic_observer & operator=(basic_observer &&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Connects an observer to a given registry.
|
||||
* @tparam Matcher Types of matchers to use to initialize the observer.
|
||||
* @param reg A valid reference to a registry.
|
||||
*/
|
||||
template<typename... Matcher>
|
||||
void connect(basic_registry<entity_type> ®, basic_collector<Matcher...>) {
|
||||
disconnect();
|
||||
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
|
||||
storage.clear();
|
||||
}
|
||||
|
||||
/*! @brief Disconnects an observer from the registry it keeps track of. */
|
||||
void disconnect() {
|
||||
if(release) {
|
||||
release(*this);
|
||||
release.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements in an observer.
|
||||
* @return Number of elements.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
|
||||
return storage.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether an observer is empty.
|
||||
* @return True if the observer is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return storage.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Direct access to the list of entities of the observer.
|
||||
*
|
||||
* The returned pointer is such that range `[data(), data() + size())` is
|
||||
* always a valid range, even if the container is empty.
|
||||
*
|
||||
* @note
|
||||
* Entities are in the reverse order as returned by the `begin`/`end`
|
||||
* iterators.
|
||||
*
|
||||
* @return A pointer to the array of entities.
|
||||
*/
|
||||
[[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT {
|
||||
return storage.data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the observer.
|
||||
*
|
||||
* The returned iterator points to the first entity of the observer. If the
|
||||
* container is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the observer.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
|
||||
return storage.basic_sparse_set<entity_type>::begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the observer.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the observer. Attempting to dereference the returned iterator results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* observer.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
|
||||
return storage.basic_sparse_set<entity_type>::end();
|
||||
}
|
||||
|
||||
/*! @brief Clears the underlying container. */
|
||||
void clear() ENTT_NOEXCEPT {
|
||||
storage.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and applies the given function object to them.
|
||||
*
|
||||
* The function object is invoked for each entity.<br/>
|
||||
* The signature of the function must be equivalent to the following form:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
for(const auto entity: *this) {
|
||||
func(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and applies the given function object to them,
|
||||
* then clears the observer.
|
||||
*
|
||||
* @sa each
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) {
|
||||
std::as_const(*this).each(std::move(func));
|
||||
clear();
|
||||
}
|
||||
|
||||
private:
|
||||
delegate<void(basic_observer &)> release;
|
||||
basic_storage<entity_type, payload_type> storage;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
509
src/entt/entity/organizer.hpp
Normal file
509
src/entt/entity/organizer.hpp
Normal file
@@ -0,0 +1,509 @@
|
||||
#ifndef ENTT_ENTITY_ORGANIZER_HPP
|
||||
#define ENTT_ENTITY_ORGANIZER_HPP
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../core/type_info.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "helper.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
|
||||
namespace internal {
|
||||
|
||||
|
||||
template<typename>
|
||||
struct is_view: std::false_type {};
|
||||
|
||||
template<typename Entity, typename... Exclude, typename... Component>
|
||||
struct is_view<basic_view<Entity, exclude_t<Exclude...>, Component...>>: std::true_type {};
|
||||
|
||||
template<typename Type>
|
||||
inline constexpr bool is_view_v = is_view<Type>::value;
|
||||
|
||||
|
||||
template<typename Type, typename Override>
|
||||
struct unpack_type {
|
||||
using ro = std::conditional_t<
|
||||
type_list_contains_v<Override, std::add_const_t<Type>> || (std::is_const_v<Type> && !type_list_contains_v<Override, std::remove_const_t<Type>>),
|
||||
type_list<std::remove_const_t<Type>>,
|
||||
type_list<>
|
||||
>;
|
||||
|
||||
using rw = std::conditional_t<
|
||||
type_list_contains_v<Override, std::remove_const_t<Type>> || (!std::is_const_v<Type> && !type_list_contains_v<Override, std::add_const_t<Type>>),
|
||||
type_list<Type>,
|
||||
type_list<>
|
||||
>;
|
||||
};
|
||||
|
||||
template<typename Entity, typename... Override>
|
||||
struct unpack_type<basic_registry<Entity>, type_list<Override...>> {
|
||||
using ro = type_list<>;
|
||||
using rw = type_list<>;
|
||||
};
|
||||
|
||||
template<typename Entity, typename... Override>
|
||||
struct unpack_type<const basic_registry<Entity>, type_list<Override...>>
|
||||
: unpack_type<basic_registry<Entity>, type_list<Override...>>
|
||||
{};
|
||||
|
||||
template<typename Entity, typename... Exclude, typename... Component, typename... Override>
|
||||
struct unpack_type<basic_view<Entity, exclude_t<Exclude...>, Component...>, type_list<Override...>> {
|
||||
using ro = type_list_cat_t<type_list<Exclude...>, typename unpack_type<Component, type_list<Override...>>::ro...>;
|
||||
using rw = type_list_cat_t<typename unpack_type<Component, type_list<Override...>>::rw...>;
|
||||
};
|
||||
|
||||
template<typename Entity, typename... Exclude, typename... Component, typename... Override>
|
||||
struct unpack_type<const basic_view<Entity, exclude_t<Exclude...>, Component...>, type_list<Override...>>
|
||||
: unpack_type<basic_view<Entity, exclude_t<Exclude...>, Component...>, type_list<Override...>>
|
||||
{};
|
||||
|
||||
|
||||
template<typename, typename>
|
||||
struct resource;
|
||||
|
||||
template<typename... Args, typename... Req>
|
||||
struct resource<type_list<Args...>, type_list<Req...>> {
|
||||
using args = type_list<std::remove_const_t<Args>...>;
|
||||
using ro = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::ro..., typename unpack_type<Req, type_list<>>::ro...>;
|
||||
using rw = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::rw..., typename unpack_type<Req, type_list<>>::rw...>;
|
||||
};
|
||||
|
||||
|
||||
template<typename... Req, typename Ret, typename... Args>
|
||||
resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource(Ret(*)(Args...));
|
||||
|
||||
template<typename... Req, typename Ret, typename Type, typename... Args>
|
||||
resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource(Ret(*)(Type &, Args...));
|
||||
|
||||
template<typename... Req, typename Ret, typename Class, typename... Args>
|
||||
resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource(Ret(Class:: *)(Args...));
|
||||
|
||||
template<typename... Req, typename Ret, typename Class, typename... Args>
|
||||
resource<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource(Ret(Class:: *)(Args...) const);
|
||||
|
||||
template<typename... Req>
|
||||
resource<type_list<>, type_list<Req...>> to_resource();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Utility class for creating a static task graph.
|
||||
*
|
||||
* This class offers minimal support (but sufficient in many cases) for creating
|
||||
* an execution graph from functions and their requirements on resources.<br/>
|
||||
* Note that the resulting tasks aren't executed in any case. This isn't the
|
||||
* goal of the tool. Instead, they are returned to the user in the form of a
|
||||
* graph that allows for safe execution.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
class basic_organizer final {
|
||||
using callback_type = void(const void *, entt::basic_registry<Entity> &);
|
||||
using prepare_type = void(entt::basic_registry<Entity> &);
|
||||
using dependency_type = std::size_t(const bool, type_info *, const std::size_t);
|
||||
|
||||
struct vertex_data final {
|
||||
std::size_t ro_count{};
|
||||
std::size_t rw_count{};
|
||||
const char *name{};
|
||||
const void *payload{};
|
||||
callback_type *callback{};
|
||||
dependency_type *dependency;
|
||||
prepare_type *prepare{};
|
||||
type_info info{};
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] static decltype(auto) extract(basic_registry<Entity> ®) {
|
||||
if constexpr(std::is_same_v<Type, basic_registry<Entity>>) {
|
||||
return reg;
|
||||
} else if constexpr(internal::is_view_v<Type>) {
|
||||
return as_view{reg};
|
||||
} else {
|
||||
return reg.template ctx_or_set<std::remove_reference_t<Type>>();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
[[nodiscard]] static auto to_args(basic_registry<Entity> ®, type_list<Args...>) {
|
||||
return std::tuple<decltype(extract<Args>(reg))...>(extract<Args>(reg)...);
|
||||
}
|
||||
|
||||
template<typename... Type>
|
||||
static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] type_info *buffer, [[maybe_unused]] const std::size_t count) {
|
||||
if constexpr(sizeof...(Type) == 0u) {
|
||||
return {};
|
||||
} else {
|
||||
type_info info[sizeof...(Type)]{type_id<Type>()...};
|
||||
const auto length = std::min(count, sizeof...(Type));
|
||||
std::copy_n(info, length, buffer);
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... RO, typename... RW>
|
||||
void track_dependencies(std::size_t index, const bool requires_registry, type_list<RO...>, type_list<RW...>) {
|
||||
dependencies[type_hash<basic_registry<Entity>>::value()].emplace_back(index, requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u));
|
||||
(dependencies[type_hash<RO>::value()].emplace_back(index, false), ...);
|
||||
(dependencies[type_hash<RW>::value()].emplace_back(index, true), ...);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<bool> adjacency_matrix() {
|
||||
const auto length = vertices.size();
|
||||
std::vector<bool> edges(length * length, false);
|
||||
|
||||
// creates the ajacency matrix
|
||||
for(const auto &deps: dependencies) {
|
||||
const auto last = deps.second.cend();
|
||||
auto it = deps.second.cbegin();
|
||||
|
||||
while(it != last) {
|
||||
if(it->second) {
|
||||
// rw item
|
||||
if(auto curr = it++; it != last) {
|
||||
if(it->second) {
|
||||
edges[curr->first * length + it->first] = true;
|
||||
} else {
|
||||
if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) {
|
||||
for(; it != next; ++it) {
|
||||
edges[curr->first * length + it->first] = true;
|
||||
edges[it->first * length + next->first] = true;
|
||||
}
|
||||
} else {
|
||||
for(; it != next; ++it) {
|
||||
edges[curr->first * length + it->first] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// ro item, possibly only on first iteration
|
||||
if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) {
|
||||
for(; it != next; ++it) {
|
||||
edges[it->first * length + next->first] = true;
|
||||
}
|
||||
} else {
|
||||
it = last;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// computes the transitive closure
|
||||
for(std::size_t vk{}; vk < length; ++vk) {
|
||||
for(std::size_t vi{}; vi < length; ++vi) {
|
||||
for(std::size_t vj{}; vj < length; ++vj) {
|
||||
edges[vi * length + vj] = edges[vi * length + vj] || (edges[vi * length + vk] && edges[vk * length + vj]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// applies the transitive reduction
|
||||
for(std::size_t vert{}; vert < length; ++vert) {
|
||||
edges[vert * length + vert] = false;
|
||||
}
|
||||
|
||||
for(std::size_t vj{}; vj < length; ++vj) {
|
||||
for(std::size_t vi{}; vi < length; ++vi) {
|
||||
if(edges[vi * length + vj]) {
|
||||
for(std::size_t vk{}; vk < length; ++vk) {
|
||||
if(edges[vj * length + vk]) {
|
||||
edges[vi * length + vk] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return edges;
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Raw task function type. */
|
||||
using function_type = callback_type;
|
||||
|
||||
/*! @brief Vertex type of a task graph defined as an adjacency list. */
|
||||
struct vertex {
|
||||
/**
|
||||
* @brief Constructs a vertex of the task graph.
|
||||
* @param vtype True if the vertex is a top-level one, false otherwise.
|
||||
* @param data The data associated with the vertex.
|
||||
* @param edges The indices of the children in the adjacency list.
|
||||
*/
|
||||
vertex(const bool vtype, vertex_data data, std::vector<std::size_t> edges)
|
||||
: is_top_level{vtype},
|
||||
node{std::move(data)},
|
||||
reachable{std::move(edges)}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Fills a buffer with the type info objects for the writable
|
||||
* resources of a vertex.
|
||||
* @param buffer A buffer pre-allocated by the user.
|
||||
* @param length The length of the user-supplied buffer.
|
||||
* @return The number of type info objects written to the buffer.
|
||||
*/
|
||||
size_type ro_dependency(type_info *buffer, const std::size_t length) const ENTT_NOEXCEPT {
|
||||
return node.dependency(false, buffer, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fills a buffer with the type info objects for the read-only
|
||||
* resources of a vertex.
|
||||
* @param buffer A buffer pre-allocated by the user.
|
||||
* @param length The length of the user-supplied buffer.
|
||||
* @return The number of type info objects written to the buffer.
|
||||
*/
|
||||
size_type rw_dependency(type_info *buffer, const std::size_t length) const ENTT_NOEXCEPT {
|
||||
return node.dependency(true, buffer, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of read-only resources of a vertex.
|
||||
* @return The number of read-only resources of the vertex.
|
||||
*/
|
||||
size_type ro_count() const ENTT_NOEXCEPT {
|
||||
return node.ro_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of writable resources of a vertex.
|
||||
* @return The number of writable resources of the vertex.
|
||||
*/
|
||||
size_type rw_count() const ENTT_NOEXCEPT {
|
||||
return node.rw_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a vertex is also a top-level one.
|
||||
* @return True if the vertex is a top-level one, false otherwise.
|
||||
*/
|
||||
bool top_level() const ENTT_NOEXCEPT {
|
||||
return is_top_level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a type info object associated with a vertex.
|
||||
* @return A properly initialized type info object.
|
||||
*/
|
||||
type_info info() const ENTT_NOEXCEPT {
|
||||
return node.info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a user defined name associated with a vertex, if any.
|
||||
* @return The user defined name associated with the vertex, if any.
|
||||
*/
|
||||
const char * name() const ENTT_NOEXCEPT {
|
||||
return node.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the function associated with a vertex.
|
||||
* @return The function associated with the vertex.
|
||||
*/
|
||||
function_type * callback() const ENTT_NOEXCEPT {
|
||||
return node.callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the payload associated with a vertex, if any.
|
||||
* @return The payload associated with the vertex, if any.
|
||||
*/
|
||||
const void * data() const ENTT_NOEXCEPT {
|
||||
return node.payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the list of nodes reachable from a given vertex.
|
||||
* @return The list of nodes reachable from the vertex.
|
||||
*/
|
||||
const std::vector<std::size_t> & children() const ENTT_NOEXCEPT {
|
||||
return reachable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepares a registry and assures that all required resources
|
||||
* are properly instantiated before using them.
|
||||
* @param reg A valid registry.
|
||||
*/
|
||||
void prepare(basic_registry<entity_type> ®) const {
|
||||
node.prepare ? node.prepare(reg) : void();
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_top_level;
|
||||
vertex_data node;
|
||||
std::vector<std::size_t> reachable;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Adds a free function to the task list.
|
||||
* @tparam Candidate Function to add to the task list.
|
||||
* @tparam Req Additional requirements and/or override resource access mode.
|
||||
* @param name Optional name to associate with the task.
|
||||
*/
|
||||
template<auto Candidate, typename... Req>
|
||||
void emplace(const char *name = nullptr) {
|
||||
using resource_type = decltype(internal::free_function_to_resource<Req...>(Candidate));
|
||||
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, basic_registry<entity_type>>;
|
||||
|
||||
callback_type *callback = +[](const void *, basic_registry<entity_type> ®) {
|
||||
std::apply(Candidate, to_args(reg, typename resource_type::args{}));
|
||||
};
|
||||
|
||||
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
|
||||
vertices.push_back({
|
||||
resource_type::ro::size,
|
||||
resource_type::rw::size,
|
||||
name,
|
||||
nullptr,
|
||||
callback,
|
||||
+[](const bool rw, type_info *buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
|
||||
+[](basic_registry<entity_type> ®) { void(to_args(reg, typename resource_type::args{})); },
|
||||
type_id<std::integral_constant<decltype(Candidate), Candidate>>()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a free function with payload or a member function with an
|
||||
* instance to the task list.
|
||||
* @tparam Candidate Function or member to add to the task list.
|
||||
* @tparam Req Additional requirements and/or override resource access mode.
|
||||
* @tparam Type Type of class or type of payload.
|
||||
* @param value_or_instance A valid object that fits the purpose.
|
||||
* @param name Optional name to associate with the task.
|
||||
*/
|
||||
template<auto Candidate, typename... Req, typename Type>
|
||||
void emplace(Type &value_or_instance, const char *name = nullptr) {
|
||||
using resource_type = decltype(internal::constrained_function_to_resource<Req...>(Candidate));
|
||||
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, basic_registry<entity_type>>;
|
||||
|
||||
callback_type *callback = +[](const void *payload, basic_registry<entity_type> ®) {
|
||||
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
|
||||
std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{})));
|
||||
};
|
||||
|
||||
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
|
||||
vertices.push_back({
|
||||
resource_type::ro::size,
|
||||
resource_type::rw::size,
|
||||
name,
|
||||
&value_or_instance,
|
||||
callback,
|
||||
+[](const bool rw, type_info *buffer, const std::size_t length) {
|
||||
return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length);
|
||||
},
|
||||
+[](basic_registry<entity_type> ®) {
|
||||
void(to_args(reg, typename resource_type::args{}));
|
||||
},
|
||||
type_id<std::integral_constant<decltype(Candidate), Candidate>>()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds an user defined function with optional payload to the task
|
||||
* list.
|
||||
* @tparam Req Additional requirements and/or override resource access mode.
|
||||
* @param func Function to add to the task list.
|
||||
* @param payload User defined arbitrary data.
|
||||
* @param name Optional name to associate with the task.
|
||||
*/
|
||||
template<typename... Req>
|
||||
void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) {
|
||||
using resource_type = internal::resource<type_list<>, type_list<Req...>>;
|
||||
track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
|
||||
vertices.push_back({
|
||||
resource_type::ro::size,
|
||||
resource_type::rw::size,
|
||||
name,
|
||||
payload,
|
||||
func,
|
||||
+[](const bool rw, type_info *buffer, const std::size_t length) {
|
||||
return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length);
|
||||
},
|
||||
nullptr,
|
||||
type_info{}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generates a task graph for the current content.
|
||||
* @return The adjacency list of the task graph.
|
||||
*/
|
||||
std::vector<vertex> graph() {
|
||||
const auto edges = adjacency_matrix();
|
||||
|
||||
// creates the adjacency list
|
||||
std::vector<vertex> adjacency_list{};
|
||||
adjacency_list.reserve(vertices.size());
|
||||
|
||||
for(std::size_t col{}, length = vertices.size(); col < length; ++col) {
|
||||
std::vector<std::size_t> reachable{};
|
||||
const auto row = col * length;
|
||||
bool is_top_level = true;
|
||||
|
||||
for(std::size_t next{}; next < length; ++next) {
|
||||
if(edges[row + next]) {
|
||||
reachable.push_back(next);
|
||||
}
|
||||
}
|
||||
|
||||
for(std::size_t next{}; next < length && is_top_level; ++next) {
|
||||
is_top_level = !edges[next * length + col];
|
||||
}
|
||||
|
||||
adjacency_list.emplace_back(is_top_level, vertices[col], std::move(reachable));
|
||||
}
|
||||
|
||||
return adjacency_list;
|
||||
}
|
||||
|
||||
/*! @brief Erases all elements from a container. */
|
||||
void clear() {
|
||||
dependencies.clear();
|
||||
vertices.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<entt::id_type, std::vector<std::pair<std::size_t, bool>>> dependencies;
|
||||
std::vector<vertex_data> vertices;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
60
src/entt/entity/poly_storage.hpp
Normal file
60
src/entt/entity/poly_storage.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef ENTT_ENTITY_POLY_STORAGE_HPP
|
||||
#define ENTT_ENTITY_POLY_STORAGE_HPP
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
#include "../core/type_info.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../poly/poly.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Basic poly storage implementation.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
struct Storage: type_list<type_info() const ENTT_NOEXCEPT> {
|
||||
/**
|
||||
* @brief Concept definition.
|
||||
* @tparam Base Opaque base class from which to inherit.
|
||||
*/
|
||||
template<typename Base>
|
||||
struct type: Base {
|
||||
/**
|
||||
* @brief Returns a type info for the contained objects.
|
||||
* @return The type info for the contained objects.
|
||||
*/
|
||||
type_info value_type() const ENTT_NOEXCEPT {
|
||||
return poly_call<0>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Concept implementation.
|
||||
* @tparam Type Type for which to generate an implementation.
|
||||
*/
|
||||
template<typename Type>
|
||||
using impl = value_list<&type_id<typename Type::value_type>>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Defines the poly storage type associate with a given entity type.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity, typename = void>
|
||||
struct poly_storage_traits {
|
||||
/*! @brief Poly storage type for the given entity type. */
|
||||
using storage_type = poly<Storage<Entity>>;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
242
src/entt/entity/runtime_view.hpp
Normal file
242
src/entt/entity/runtime_view.hpp
Normal file
@@ -0,0 +1,242 @@
|
||||
#ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP
|
||||
#define ENTT_ENTITY_RUNTIME_VIEW_HPP
|
||||
|
||||
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "sparse_set.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Runtime view.
|
||||
*
|
||||
* Runtime views iterate over those entities that have at least all the given
|
||||
* components in their bags. During initialization, a runtime view looks at the
|
||||
* number of entities available for each component and picks up a reference to
|
||||
* the smallest set of candidate entities in order to get a performance boost
|
||||
* when iterate.<br/>
|
||||
* Order of elements during iterations are highly dependent on the order of the
|
||||
* underlying data structures. See sparse_set and its specializations for more
|
||||
* details.
|
||||
*
|
||||
* @b Important
|
||||
*
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all the other cases, modifying the pools of the given components in any
|
||||
* way invalidates all the iterators and using them results in undefined
|
||||
* behavior.
|
||||
*
|
||||
* @note
|
||||
* Views share references to the underlying data structures of the registry that
|
||||
* generated them. Therefore any change to the entities and to the components
|
||||
* made by means of the registry are immediately reflected by the views, unless
|
||||
* a pool was missing when the view was built (in this case, the view won't
|
||||
* have a valid reference and won't be updated accordingly).
|
||||
*
|
||||
* @warning
|
||||
* Lifetime of a view must not overcome that of the registry that generated it.
|
||||
* In any other case, attempting to use a view results in undefined behavior.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
class basic_runtime_view final {
|
||||
using underlying_iterator = typename basic_sparse_set<Entity>::iterator;
|
||||
|
||||
class view_iterator final {
|
||||
friend class basic_runtime_view<Entity>;
|
||||
|
||||
view_iterator(const std::vector<const basic_sparse_set<Entity> *> &cpools, const std::vector<const basic_sparse_set<Entity> *> &ignore, underlying_iterator curr) ENTT_NOEXCEPT
|
||||
: pools{&cpools},
|
||||
filter{&ignore},
|
||||
it{curr}
|
||||
{
|
||||
if(it != (*pools)[0]->end() && !valid()) {
|
||||
++(*this);
|
||||
}
|
||||
}
|
||||
|
||||
[[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::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;
|
||||
|
||||
view_iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
view_iterator & operator++() {
|
||||
while(++it != (*pools)[0]->end() && !valid());
|
||||
return *this;
|
||||
}
|
||||
|
||||
view_iterator operator++(int) {
|
||||
view_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
view_iterator & operator--() ENTT_NOEXCEPT {
|
||||
while(--it != (*pools)[0]->begin() && !valid());
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
[[nodiscard]] pointer operator->() const {
|
||||
return it.operator->();
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<const basic_sparse_set<Entity> *> *pools;
|
||||
const std::vector<const basic_sparse_set<Entity> *> *filter;
|
||||
underlying_iterator it;
|
||||
};
|
||||
|
||||
[[nodiscard]] bool valid() const {
|
||||
return !pools.empty() && pools.front();
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Bidirectional iterator type. */
|
||||
using iterator = view_iterator;
|
||||
|
||||
/*! @brief Default constructor to use to create empty, invalid views. */
|
||||
basic_runtime_view() ENTT_NOEXCEPT
|
||||
: pools{},
|
||||
filter{}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Constructs a runtime view from a set of storage classes.
|
||||
* @param cpools The storage for the types to iterate.
|
||||
* @param epools The storage for the types used to filter the view.
|
||||
*/
|
||||
basic_runtime_view(std::vector<const basic_sparse_set<Entity> *> cpools, std::vector<const basic_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());
|
||||
});
|
||||
|
||||
// brings the best candidate (if any) on front of the vector
|
||||
std::rotate(pools.begin(), it, pools.end());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Estimates the number of entities iterated by the view.
|
||||
* @return Estimated number of entities iterated by the view.
|
||||
*/
|
||||
[[nodiscard]] size_type size_hint() const {
|
||||
return valid() ? pools.front()->size() : size_type{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity that has the given
|
||||
* components.
|
||||
*
|
||||
* The returned iterator points to the first entity that has the given
|
||||
* components. If the view is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const {
|
||||
return valid() ? iterator{pools, filter, pools[0]->begin()} : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given components.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity that
|
||||
* has the given components. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const {
|
||||
return valid() ? iterator{pools, filter, pools[0]->end()} : iterator{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a view contains an entity.
|
||||
* @param entt A valid entity identifier.
|
||||
* @return True if the view contains the given entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(const entity_type entt) const {
|
||||
return valid() && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); })
|
||||
&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); });
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and applies the given function object to them.
|
||||
*
|
||||
* The function object is invoked for each entity. It is provided only with
|
||||
* the entity itself. To get the components, users can use the registry with
|
||||
* which the view was built.<br/>
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
for(const auto entity: *this) {
|
||||
func(entity);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<const basic_sparse_set<Entity> *> pools;
|
||||
std::vector<const basic_sparse_set<Entity> *> filter;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
572
src/entt/entity/snapshot.hpp
Normal file
572
src/entt/entity/snapshot.hpp
Normal file
@@ -0,0 +1,572 @@
|
||||
#ifndef ENTT_ENTITY_SNAPSHOT_HPP
|
||||
#define ENTT_ENTITY_SNAPSHOT_HPP
|
||||
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "registry.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Utility class to create snapshots from a registry.
|
||||
*
|
||||
* A _snapshot_ can be either a dump of the entire registry or a narrower
|
||||
* selection of components of interest.<br/>
|
||||
* This type can be used in both cases if provided with a correctly configured
|
||||
* output archive.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
class basic_snapshot {
|
||||
using 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 {
|
||||
const auto view = reg->template view<std::add_const_t<Component>>();
|
||||
archive(typename traits_type::entity_type(sz));
|
||||
|
||||
while(first != last) {
|
||||
const auto entt = *(first++);
|
||||
|
||||
if(reg->template all_of<Component>(entt)) {
|
||||
std::apply(archive, std::tuple_cat(std::make_tuple(entt), view.get(entt)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Component, typename Archive, typename It, std::size_t... Index>
|
||||
void component(Archive &archive, It first, It last, std::index_sequence<Index...>) const {
|
||||
std::array<std::size_t, sizeof...(Index)> size{};
|
||||
auto begin = first;
|
||||
|
||||
while(begin != last) {
|
||||
const auto entt = *(begin++);
|
||||
((reg->template all_of<Component>(entt) ? ++size[Index] : size[Index]), ...);
|
||||
}
|
||||
|
||||
(get<Component>(archive, size[Index], first, last), ...);
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
|
||||
/**
|
||||
* @brief Constructs an instance that is bound to a given registry.
|
||||
* @param source A valid reference to a registry.
|
||||
*/
|
||||
basic_snapshot(const basic_registry<entity_type> &source) ENTT_NOEXCEPT
|
||||
: reg{&source}
|
||||
{}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
basic_snapshot(basic_snapshot &&) = default;
|
||||
|
||||
/*! @brief Default move assignment operator. @return This snapshot. */
|
||||
basic_snapshot & operator=(basic_snapshot &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Puts aside all the entities from the underlying registry.
|
||||
*
|
||||
* Entities are serialized along with their versions. Destroyed entities are
|
||||
* taken in consideration as well by this function.
|
||||
*
|
||||
* @tparam Archive Type of output archive.
|
||||
* @param archive A valid reference to an output archive.
|
||||
* @return An object of this type to continue creating the snapshot.
|
||||
*/
|
||||
template<typename Archive>
|
||||
const basic_snapshot & entities(Archive &archive) const {
|
||||
const auto sz = reg->size();
|
||||
|
||||
archive(typename traits_type::entity_type(sz));
|
||||
|
||||
for(auto first = reg->data(), last = first + sz; first != last; ++first) {
|
||||
archive(*first);
|
||||
}
|
||||
|
||||
archive(reg->destroyed());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Puts aside the given components.
|
||||
*
|
||||
* Each instance is serialized together with the entity to which it belongs.
|
||||
* Entities are serialized along with their versions.
|
||||
*
|
||||
* @tparam Component Types of components to serialize.
|
||||
* @tparam Archive Type of output archive.
|
||||
* @param archive A valid reference to an output archive.
|
||||
* @return An object of this type to continue creating the snapshot.
|
||||
*/
|
||||
template<typename... Component, typename Archive>
|
||||
const basic_snapshot & component(Archive &archive) const {
|
||||
if constexpr(sizeof...(Component) == 1u) {
|
||||
const auto view = reg->template view<const Component...>();
|
||||
(component<Component>(archive, view.data(), view.data() + view.size()), ...);
|
||||
return *this;
|
||||
} else {
|
||||
(component<Component>(archive), ...);
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Puts aside the given components for the entities in a range.
|
||||
*
|
||||
* Each instance is serialized together with the entity to which it belongs.
|
||||
* Entities are serialized along with their versions.
|
||||
*
|
||||
* @tparam Component Types of components to serialize.
|
||||
* @tparam Archive Type of output archive.
|
||||
* @tparam It Type of input iterator.
|
||||
* @param archive A valid reference to an output archive.
|
||||
* @param first An iterator to the first element of the range to serialize.
|
||||
* @param last An iterator past the last element of the range to serialize.
|
||||
* @return An object of this type to continue creating the snapshot.
|
||||
*/
|
||||
template<typename... Component, typename Archive, typename It>
|
||||
const basic_snapshot & component(Archive &archive, It first, It last) const {
|
||||
component<Component...>(archive, first, last, std::index_sequence_for<Component...>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
const basic_registry<entity_type> *reg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Utility class to restore a snapshot as a whole.
|
||||
*
|
||||
* A snapshot loader requires that the destination registry be empty and loads
|
||||
* all the data at once while keeping intact the identifiers that the entities
|
||||
* originally had.<br/>
|
||||
* An example of use is the implementation of a save/restore utility.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
class basic_snapshot_loader {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
template<typename Type, typename Archive>
|
||||
void assign(Archive &archive) const {
|
||||
typename traits_type::entity_type length{};
|
||||
archive(length);
|
||||
|
||||
entity_type entt{};
|
||||
|
||||
if constexpr(std::tuple_size_v<decltype(reg->template view<Type>().get({}))> == 0) {
|
||||
while(length--) {
|
||||
archive(entt);
|
||||
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);
|
||||
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;
|
||||
|
||||
/*! @brief Default move assignment operator. @return This loader. */
|
||||
basic_snapshot_loader & operator=(basic_snapshot_loader &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Restores entities that were in use during serialization.
|
||||
*
|
||||
* This function restores the entities that were in use during serialization
|
||||
* and gives them the versions they originally had.
|
||||
*
|
||||
* @tparam Archive Type of input archive.
|
||||
* @param archive A valid reference to an input archive.
|
||||
* @return A valid loader to continue restoring data.
|
||||
*/
|
||||
template<typename Archive>
|
||||
const basic_snapshot_loader & entities(Archive &archive) const {
|
||||
typename traits_type::entity_type length{};
|
||||
|
||||
archive(length);
|
||||
std::vector<entity_type> all(length);
|
||||
|
||||
for(decltype(length) pos{}; pos < length; ++pos) {
|
||||
archive(all[pos]);
|
||||
}
|
||||
|
||||
entity_type destroyed;
|
||||
archive(destroyed);
|
||||
|
||||
reg->assign(all.cbegin(), all.cend(), destroyed);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restores components and assigns them to the right entities.
|
||||
*
|
||||
* The template parameter list must be exactly the same used during
|
||||
* serialization. In the event that the entity to which the component is
|
||||
* assigned doesn't exist yet, the loader will take care to create it with
|
||||
* the version it originally had.
|
||||
*
|
||||
* @tparam Component Types of components to restore.
|
||||
* @tparam Archive Type of input archive.
|
||||
* @param archive A valid reference to an input archive.
|
||||
* @return A valid loader to continue restoring data.
|
||||
*/
|
||||
template<typename... Component, typename Archive>
|
||||
const basic_snapshot_loader & component(Archive &archive) const {
|
||||
(assign<Component>(archive), ...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys those entities that have no components.
|
||||
*
|
||||
* In case all the entities were serialized but only part of the components
|
||||
* was saved, it could happen that some of the entities have no components
|
||||
* once restored.<br/>
|
||||
* This functions helps to identify and destroy those entities.
|
||||
*
|
||||
* @return A valid loader to continue restoring data.
|
||||
*/
|
||||
const basic_snapshot_loader & orphans() const {
|
||||
reg->orphans([this](const auto entt) {
|
||||
reg->destroy(entt);
|
||||
});
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
basic_registry<entity_type> *reg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Utility class for _continuous loading_.
|
||||
*
|
||||
* A _continuous loader_ is designed to load data from a source registry to a
|
||||
* (possibly) non-empty destination. The loader can accommodate in a registry
|
||||
* more than one snapshot in a sort of _continuous loading_ that updates the
|
||||
* destination one step at a time.<br/>
|
||||
* Identifiers that entities originally had are not transferred to the target.
|
||||
* Instead, the loader maps remote identifiers to local ones while restoring a
|
||||
* snapshot.<br/>
|
||||
* An example of use is the implementation of a client-server applications with
|
||||
* the requirement of transferring somehow parts of the representation side to
|
||||
* side.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
class basic_continuous_loader {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
void destroy(Entity entt) {
|
||||
if(const auto it = remloc.find(entt); it == remloc.cend()) {
|
||||
const auto local = reg->create();
|
||||
remloc.emplace(entt, std::make_pair(local, true));
|
||||
reg->destroy(local);
|
||||
}
|
||||
}
|
||||
|
||||
void restore(Entity entt) {
|
||||
const auto it = remloc.find(entt);
|
||||
|
||||
if(it == remloc.cend()) {
|
||||
const auto local = reg->create();
|
||||
remloc.emplace(entt, std::make_pair(local, true));
|
||||
} else {
|
||||
if(!reg->valid(remloc[entt].first)) {
|
||||
remloc[entt].first = reg->create();
|
||||
}
|
||||
|
||||
// set the dirty flag
|
||||
remloc[entt].second = true;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
auto update(int, Container &container)
|
||||
-> decltype(typename Container::mapped_type{}, void()) {
|
||||
// map like container
|
||||
Container other;
|
||||
|
||||
for(auto &&pair: container) {
|
||||
using first_type = std::remove_const_t<typename std::decay_t<decltype(pair)>::first_type>;
|
||||
using second_type = typename std::decay_t<decltype(pair)>::second_type;
|
||||
|
||||
if constexpr(std::is_same_v<first_type, entity_type> && std::is_same_v<second_type, entity_type>) {
|
||||
other.emplace(map(pair.first), map(pair.second));
|
||||
} else if constexpr(std::is_same_v<first_type, entity_type>) {
|
||||
other.emplace(map(pair.first), std::move(pair.second));
|
||||
} else {
|
||||
static_assert(std::is_same_v<second_type, entity_type>, "Neither the key nor the value are of entity type");
|
||||
other.emplace(std::move(pair.first), map(pair.second));
|
||||
}
|
||||
}
|
||||
|
||||
std::swap(container, other);
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
auto update(char, Container &container)
|
||||
-> decltype(typename Container::value_type{}, void()) {
|
||||
// vector like container
|
||||
static_assert(std::is_same_v<typename Container::value_type, entity_type>, "Invalid value type");
|
||||
|
||||
for(auto &&entt: container) {
|
||||
entt = map(entt);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Other, typename Type, typename Member>
|
||||
void update([[maybe_unused]] Other &instance, [[maybe_unused]] Member Type:: *member) {
|
||||
if constexpr(!std::is_same_v<Other, Type>) {
|
||||
return;
|
||||
} else if constexpr(std::is_same_v<Member, entity_type>) {
|
||||
instance.*member = map(instance.*member);
|
||||
} else {
|
||||
// maybe a container? let's try...
|
||||
update(0, instance.*member);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
void remove_if_exists() {
|
||||
for(auto &&ref: remloc) {
|
||||
const auto local = ref.second.first;
|
||||
|
||||
if(reg->valid(local)) {
|
||||
reg->template remove_if_exists<Component>(local);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Other, typename Archive, typename... Type, typename... Member>
|
||||
void assign(Archive &archive, [[maybe_unused]] Member Type:: *... member) {
|
||||
typename traits_type::entity_type length{};
|
||||
archive(length);
|
||||
|
||||
entity_type entt{};
|
||||
|
||||
if constexpr(std::tuple_size_v<decltype(reg->template view<Other>().get({}))> == 0) {
|
||||
while(length--) {
|
||||
archive(entt);
|
||||
restore(entt);
|
||||
reg->template emplace_or_replace<Other>(map(entt));
|
||||
}
|
||||
} else {
|
||||
Other instance{};
|
||||
|
||||
while(length--) {
|
||||
archive(entt, instance);
|
||||
(update(instance, member), ...);
|
||||
restore(entt);
|
||||
reg->template emplace_or_replace<Other>(map(entt), std::move(instance));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
|
||||
/**
|
||||
* @brief Constructs an instance that is bound to a given registry.
|
||||
* @param source A valid reference to a registry.
|
||||
*/
|
||||
basic_continuous_loader(basic_registry<entity_type> &source) ENTT_NOEXCEPT
|
||||
: reg{&source}
|
||||
{}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
basic_continuous_loader(basic_continuous_loader &&) = default;
|
||||
|
||||
/*! @brief Default move assignment operator. @return This loader. */
|
||||
basic_continuous_loader & operator=(basic_continuous_loader &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Restores entities that were in use during serialization.
|
||||
*
|
||||
* This function restores the entities that were in use during serialization
|
||||
* and creates local counterparts for them if required.
|
||||
*
|
||||
* @tparam Archive Type of input archive.
|
||||
* @param archive A valid reference to an input archive.
|
||||
* @return A non-const reference to this loader.
|
||||
*/
|
||||
template<typename Archive>
|
||||
basic_continuous_loader & entities(Archive &archive) {
|
||||
typename 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);
|
||||
}
|
||||
}
|
||||
|
||||
// discards the head of the list of destroyed entities
|
||||
archive(entt);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restores components and assigns them to the right entities.
|
||||
*
|
||||
* The template parameter list must be exactly the same used during
|
||||
* serialization. In the event that the entity to which the component is
|
||||
* assigned doesn't exist yet, the loader will take care to create a local
|
||||
* counterpart for it.<br/>
|
||||
* Members can be either data members of type entity_type or containers of
|
||||
* entities. In both cases, the loader will visit them and update the
|
||||
* entities by replacing each one with its local counterpart.
|
||||
*
|
||||
* @tparam Component Type of component to restore.
|
||||
* @tparam Archive Type of input archive.
|
||||
* @tparam Type Types of components to update with local counterparts.
|
||||
* @tparam Member Types of members to update with their local counterparts.
|
||||
* @param archive A valid reference to an input archive.
|
||||
* @param member Members to update with their local counterparts.
|
||||
* @return A non-const reference to this loader.
|
||||
*/
|
||||
template<typename... Component, typename Archive, typename... Type, typename... Member>
|
||||
basic_continuous_loader & component(Archive &archive, Member Type:: *... member) {
|
||||
(remove_if_exists<Component>(), ...);
|
||||
(assign<Component>(archive, member...), ...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helps to purge entities that no longer have a conterpart.
|
||||
*
|
||||
* Users should invoke this member function after restoring each snapshot,
|
||||
* unless they know exactly what they are doing.
|
||||
*
|
||||
* @return A non-const reference to this loader.
|
||||
*/
|
||||
basic_continuous_loader & shrink() {
|
||||
auto it = remloc.begin();
|
||||
|
||||
while(it != remloc.cend()) {
|
||||
const auto local = it->second.first;
|
||||
bool &dirty = it->second.second;
|
||||
|
||||
if(dirty) {
|
||||
dirty = false;
|
||||
++it;
|
||||
} else {
|
||||
if(reg->valid(local)) {
|
||||
reg->destroy(local);
|
||||
}
|
||||
|
||||
it = remloc.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys those entities that have no components.
|
||||
*
|
||||
* In case all the entities were serialized but only part of the components
|
||||
* was saved, it could happen that some of the entities have no components
|
||||
* once restored.<br/>
|
||||
* This functions helps to identify and destroy those entities.
|
||||
*
|
||||
* @return A non-const reference to this loader.
|
||||
*/
|
||||
basic_continuous_loader & orphans() {
|
||||
reg->orphans([this](const auto entt) {
|
||||
reg->destroy(entt);
|
||||
});
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tests if a loader knows about a given entity.
|
||||
* @param entt An entity identifier.
|
||||
* @return True if `entity` is managed by the loader, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contains(entity_type entt) const ENTT_NOEXCEPT {
|
||||
return (remloc.find(entt) != remloc.cend());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the identifier to which an entity refers.
|
||||
* @param entt An entity identifier.
|
||||
* @return The local identifier if any, the null entity otherwise.
|
||||
*/
|
||||
[[nodiscard]] entity_type map(entity_type entt) const ENTT_NOEXCEPT {
|
||||
const auto it = remloc.find(entt);
|
||||
entity_type other = null;
|
||||
|
||||
if(it != remloc.cend()) {
|
||||
other = it->second.first;
|
||||
}
|
||||
|
||||
return other;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<entity_type, std::pair<entity_type, bool>> remloc;
|
||||
basic_registry<entity_type> *reg;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
820
src/entt/entity/storage.hpp
Normal file
820
src/entt/entity/storage.hpp
Normal file
@@ -0,0 +1,820 @@
|
||||
#ifndef ENTT_ENTITY_STORAGE_HPP
|
||||
#define ENTT_ENTITY_STORAGE_HPP
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/algorithm.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../signal/sigh.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "sparse_set.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Basic storage implementation.
|
||||
*
|
||||
* This class is a refinement of a sparse set that associates an object to an
|
||||
* entity. The main purpose of this class is to extend sparse sets to store
|
||||
* components in a registry. It guarantees fast access both to the elements and
|
||||
* to the entities.
|
||||
*
|
||||
* @note
|
||||
* Entities and objects have the same order. It's guaranteed both in case of raw
|
||||
* access (either to entities or objects) and when using random or input access
|
||||
* iterators.
|
||||
*
|
||||
* @note
|
||||
* 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. Therefore, many of the functions
|
||||
* normally available for non-empty types will not be available for empty ones.
|
||||
*
|
||||
* @sa sparse_set<Entity>
|
||||
*
|
||||
* @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>
|
||||
class basic_storage: public basic_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 = basic_sparse_set<Entity>;
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
template<typename Value>
|
||||
class storage_iterator final {
|
||||
friend class basic_storage<Entity, Type>;
|
||||
|
||||
using instance_type = constness_as_t<std::vector<Type>, Value>;
|
||||
using index_type = typename traits_type::difference_type;
|
||||
|
||||
storage_iterator(instance_type &ref, const index_type idx) ENTT_NOEXCEPT
|
||||
: instances{&ref}, index{idx}
|
||||
{}
|
||||
|
||||
public:
|
||||
using difference_type = index_type;
|
||||
using value_type = Value;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
storage_iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
storage_iterator & operator++() ENTT_NOEXCEPT {
|
||||
return --index, *this;
|
||||
}
|
||||
|
||||
storage_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
storage_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
storage_iterator & operator--() ENTT_NOEXCEPT {
|
||||
return ++index, *this;
|
||||
}
|
||||
|
||||
storage_iterator operator--(int) ENTT_NOEXCEPT {
|
||||
storage_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
storage_iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
|
||||
index -= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
storage_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
||||
storage_iterator copy = *this;
|
||||
return (copy += value);
|
||||
}
|
||||
|
||||
storage_iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
|
||||
return (*this += -value);
|
||||
}
|
||||
|
||||
storage_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return (*this + -value);
|
||||
}
|
||||
|
||||
difference_type operator-(const storage_iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.index - index;
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
|
||||
const auto pos = size_type(index-value-1);
|
||||
return (*instances)[pos];
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const storage_iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.index == index;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator!=(const storage_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator<(const storage_iterator &other) const ENTT_NOEXCEPT {
|
||||
return index > other.index;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator>(const storage_iterator &other) const ENTT_NOEXCEPT {
|
||||
return index < other.index;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator<=(const storage_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this > other);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator>=(const storage_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this < other);
|
||||
}
|
||||
|
||||
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
|
||||
const auto pos = size_type(index-1u);
|
||||
return &(*instances)[pos];
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
private:
|
||||
instance_type *instances;
|
||||
index_type index;
|
||||
};
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @copybrief basic_sparse_set::swap_at
|
||||
* @param lhs A valid position of an entity within storage.
|
||||
* @param rhs A valid position of an entity within storage.
|
||||
*/
|
||||
void swap_at(const std::size_t lhs, const std::size_t rhs) {
|
||||
std::swap(instances[lhs], instances[rhs]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copybrief basic_sparse_set::swap_and_pop
|
||||
* @param pos A valid position of an entity within storage.
|
||||
*/
|
||||
void swap_and_pop(const std::size_t pos, void *) {
|
||||
auto other = std::move(instances.back());
|
||||
instances[pos] = std::move(other);
|
||||
instances.pop_back();
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Type of the objects assigned to entities. */
|
||||
using value_type = Type;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = storage_iterator<Type>;
|
||||
/*! @brief Constant random access iterator type. */
|
||||
using const_iterator = storage_iterator<const Type>;
|
||||
/*! @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.
|
||||
*
|
||||
* If the new capacity is greater than the current capacity, new storage is
|
||||
* allocated, otherwise the method does nothing.
|
||||
*
|
||||
* @param cap Desired capacity.
|
||||
*/
|
||||
void reserve(const size_type cap) {
|
||||
underlying_type::reserve(cap);
|
||||
instances.reserve(cap);
|
||||
}
|
||||
|
||||
/*! @brief Requests the removal of unused capacity. */
|
||||
void shrink_to_fit() {
|
||||
underlying_type::shrink_to_fit();
|
||||
instances.shrink_to_fit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Direct access to the array of objects.
|
||||
*
|
||||
* The returned pointer is such that range `[raw(), raw() + size())` is
|
||||
* always a valid range, even if the container is empty.
|
||||
*
|
||||
* @note
|
||||
* Objects are in the reverse order as returned by the `begin`/`end`
|
||||
* iterators.
|
||||
*
|
||||
* @return A pointer to the array of objects.
|
||||
*/
|
||||
[[nodiscard]] const value_type * raw() const ENTT_NOEXCEPT {
|
||||
return instances.data();
|
||||
}
|
||||
|
||||
/*! @copydoc raw */
|
||||
[[nodiscard]] value_type * raw() ENTT_NOEXCEPT {
|
||||
return const_cast<value_type *>(std::as_const(*this).raw());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
*
|
||||
* The returned iterator points to the first instance of the internal array.
|
||||
* If the storage is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first instance of the internal array.
|
||||
*/
|
||||
[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
|
||||
const typename traits_type::difference_type pos = underlying_type::size();
|
||||
return const_iterator{instances, pos};
|
||||
}
|
||||
|
||||
/*! @copydoc cbegin */
|
||||
[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/*! @copydoc begin */
|
||||
[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
|
||||
const typename traits_type::difference_type pos = underlying_type::size();
|
||||
return iterator{instances, pos};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end.
|
||||
*
|
||||
* The returned iterator points to the element following the last instance
|
||||
* of the internal array. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* internal array.
|
||||
*/
|
||||
[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
|
||||
return const_iterator{instances, {}};
|
||||
}
|
||||
|
||||
/*! @copydoc cend */
|
||||
[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/*! @copydoc end */
|
||||
[[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();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the object assigned to an entity.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an entity that doesn't belong to the storage results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @param entt A valid entity identifier.
|
||||
* @return The object assigned to the entity.
|
||||
*/
|
||||
[[nodiscard]] const value_type & get(const entity_type entt) const {
|
||||
return instances[underlying_type::index(entt)];
|
||||
}
|
||||
|
||||
/*! @copydoc get */
|
||||
[[nodiscard]] value_type & get(const entity_type entt) {
|
||||
return const_cast<value_type &>(std::as_const(*this).get(entt));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns an entity to a storage and constructs its object.
|
||||
*
|
||||
* This version accept both types that can be constructed in place directly
|
||||
* and types like aggregates that do not work well with a placement new as
|
||||
* performed usually under the hood during an _emplace back_.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an entity that already belongs to the storage results
|
||||
* in undefined behavior.
|
||||
*
|
||||
* @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 A reference to the newly created object.
|
||||
*/
|
||||
template<typename... Args>
|
||||
value_type & emplace(const entity_type entt, Args &&... args) {
|
||||
if constexpr(std::is_aggregate_v<value_type>) {
|
||||
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::emplace(entt);
|
||||
return instances.back();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates the instance assigned to a given entity in-place.
|
||||
* @tparam Func Types of the function objects to invoke.
|
||||
* @param entity A valid entity identifier.
|
||||
* @param func Valid function objects.
|
||||
* @return A reference to the updated instance.
|
||||
*/
|
||||
template<typename... Func>
|
||||
decltype(auto) patch(const entity_type entity, Func &&... func) {
|
||||
auto &&instance = instances[this->index(entity)];
|
||||
(std::forward<Func>(func)(instance), ...);
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @tparam It Type of input iterator.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
* @param value An instance of the object to construct.
|
||||
*/
|
||||
template<typename It>
|
||||
void insert(It first, It last, const value_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 constructs their
|
||||
* objects from a given range.
|
||||
*
|
||||
* @sa construct
|
||||
*
|
||||
* @tparam EIt Type of input iterator.
|
||||
* @tparam CIt Type of input iterator.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
* @param from An iterator to the first element of the range of objects.
|
||||
* @param to An iterator past the last element of the range of objects.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort elements according to the given comparison function.
|
||||
*
|
||||
* The comparison function object must return `true` if the first element
|
||||
* is _less_ than the second one, `false` otherwise. The signature of the
|
||||
* comparison function should be equivalent to one of the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* bool(const Entity, const Entity);
|
||||
* bool(const Type &, const Type &);
|
||||
* @endcode
|
||||
*
|
||||
* Moreover, the comparison function object shall induce a
|
||||
* _strict weak ordering_ on the values.
|
||||
*
|
||||
* The sort function oject must offer a member function template
|
||||
* `operator()` that accepts three arguments:
|
||||
*
|
||||
* * An iterator to the first element of the range to sort.
|
||||
* * An iterator past the last element of the range to sort.
|
||||
* * A comparison function to use to compare the elements.
|
||||
*
|
||||
* @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.
|
||||
* @tparam Args Types of arguments to forward to the sort function object.
|
||||
* @param count Number of elements to sort.
|
||||
* @param compare A valid comparison function object.
|
||||
* @param algo A valid sort function object.
|
||||
* @param args Arguments to forward to the sort function object, if any.
|
||||
*/
|
||||
template<typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort_n(const size_type count, Compare compare, Sort algo = Sort{}, Args &&... args) {
|
||||
if constexpr(std::is_invocable_v<Compare, const value_type &, const value_type &>) {
|
||||
underlying_type::sort_n(count, [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_n(count, std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort all elements according to the given comparison function.
|
||||
*
|
||||
* @sa sort_n
|
||||
*
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Sort Type of sort function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function object.
|
||||
* @param compare A valid comparison function object.
|
||||
* @param algo A valid sort function object.
|
||||
* @param args Arguments to forward to the sort function object, if any.
|
||||
*/
|
||||
template<typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(Compare compare, Sort algo = Sort{}, Args &&... args) {
|
||||
sort_n(this->size(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<value_type> instances;
|
||||
};
|
||||
|
||||
|
||||
/*! @copydoc basic_storage */
|
||||
template<typename Entity, typename Type>
|
||||
class basic_storage<Entity, Type, std::enable_if_t<is_empty_v<Type>>>: public basic_sparse_set<Entity> {
|
||||
using underlying_type = basic_sparse_set<Entity>;
|
||||
|
||||
public:
|
||||
/*! @brief Type of the objects assigned to entities. */
|
||||
using value_type = Type;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
|
||||
/**
|
||||
* @brief Fake get function.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an entity that doesn't belong to the storage results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @param entt A valid entity identifier.
|
||||
*/
|
||||
void get([[maybe_unused]] const entity_type entt) const {
|
||||
ENTT_ASSERT(this->contains(entt));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns an entity to a storage and constructs its object.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an entity that already belongs to the storage results
|
||||
* in undefined behavior.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
template<typename... Args>
|
||||
void emplace(const entity_type entt, Args &&... args) {
|
||||
[[maybe_unused]] value_type instance{std::forward<Args>(args)...};
|
||||
underlying_type::emplace(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates the instance assigned to a given entity in-place.
|
||||
* @tparam Func Types of the function objects to invoke.
|
||||
* @param entity A valid entity identifier.
|
||||
* @param func Valid function objects.
|
||||
*/
|
||||
template<typename... Func>
|
||||
void patch([[maybe_unused]] const entity_type entity, Func &&... func) {
|
||||
ENTT_ASSERT(this->contains(entity));
|
||||
(std::forward<Func>(func)(), ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns one or more entities to a storage.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to assign an entity that already belongs to the storage
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @tparam It Type of input iterator.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
*/
|
||||
template<typename It>
|
||||
void insert(It first, It last, const value_type & = {}) {
|
||||
underlying_type::insert(first, last);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Mixin type to use to wrap basic storage classes.
|
||||
* @tparam Type The type of the underlying storage.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct storage_adapter_mixin: Type {
|
||||
static_assert(std::is_same_v<typename Type::value_type, std::decay_t<typename Type::value_type>>, "Invalid object type");
|
||||
|
||||
/*! @brief Type of the objects assigned to entities. */
|
||||
using value_type = typename Type::value_type;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename Type::entity_type;
|
||||
|
||||
/**
|
||||
* @brief Assigns entities to a storage.
|
||||
* @tparam Args Types of arguments to use to construct the object.
|
||||
* @param entity A valid entity identifier.
|
||||
* @param args Parameters to use to initialize the object.
|
||||
* @return A reference to the newly created object.
|
||||
*/
|
||||
template<typename... Args>
|
||||
decltype(auto) emplace(basic_registry<entity_type> &, const entity_type entity, Args &&... args) {
|
||||
return Type::emplace(entity, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns entities to a storage.
|
||||
* @tparam It Type of input iterator.
|
||||
* @tparam Args Types of arguments to use to construct the objects assigned
|
||||
* to the entities.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
* @param args Parameters to use to initialize the objects assigned to the
|
||||
* entities.
|
||||
*/
|
||||
template<typename It, typename... Args>
|
||||
void insert(basic_registry<entity_type> &, It first, It last, Args &&... args) {
|
||||
Type::insert(first, last, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Patches the given instance for an entity.
|
||||
* @tparam Func Types of the function objects to invoke.
|
||||
* @param entity A valid entity identifier.
|
||||
* @param func Valid function objects.
|
||||
* @return A reference to the patched instance.
|
||||
*/
|
||||
template<typename... Func>
|
||||
decltype(auto) patch(basic_registry<entity_type> &, const entity_type entity, Func &&... func) {
|
||||
return Type::patch(entity, std::forward<Func>(func)...);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Mixin type to use to add signal support to storage types.
|
||||
* @tparam Type The type of the underlying storage.
|
||||
*/
|
||||
template<typename Type>
|
||||
class sigh_storage_mixin final: public Type {
|
||||
/**
|
||||
* @copybrief basic_sparse_set::swap_and_pop
|
||||
* @param pos A valid position of an entity within storage.
|
||||
* @param ud Optional user data that are forwarded as-is to derived classes.
|
||||
*/
|
||||
void swap_and_pop(const std::size_t pos, void *ud) final {
|
||||
ENTT_ASSERT(ud != nullptr);
|
||||
const auto entity = basic_sparse_set<typename Type::entity_type>::operator[](pos);
|
||||
destruction.publish(*static_cast<basic_registry<typename Type::entity_type> *>(ud), entity);
|
||||
// the position may have changed due to the actions of a listener
|
||||
Type::swap_and_pop(this->index(entity), ud);
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying value type. */
|
||||
using value_type = typename Type::value_type;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename Type::entity_type;
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object.
|
||||
*
|
||||
* The sink returned by this function can be used to receive notifications
|
||||
* whenever a new instance is created and assigned to an entity.<br/>
|
||||
* The function type for a listener is equivalent to:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(basic_registry<entity_type> &, entity_type);
|
||||
* @endcode
|
||||
*
|
||||
* Listeners are invoked **after** the object has been assigned to the
|
||||
* entity.
|
||||
*
|
||||
* @sa sink
|
||||
*
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
[[nodiscard]] auto on_construct() ENTT_NOEXCEPT {
|
||||
return sink{construction};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object.
|
||||
*
|
||||
* The sink returned by this function can be used to receive notifications
|
||||
* whenever an instance is explicitly updated.<br/>
|
||||
* The function type for a listener is equivalent to:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(basic_registry<entity_type> &, entity_type);
|
||||
* @endcode
|
||||
*
|
||||
* Listeners are invoked **after** the object has been updated.
|
||||
*
|
||||
* @sa sink
|
||||
*
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
[[nodiscard]] auto on_update() ENTT_NOEXCEPT {
|
||||
return sink{update};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object.
|
||||
*
|
||||
* The sink returned by this function can be used to receive notifications
|
||||
* whenever an instance is removed from an entity and thus destroyed.<br/>
|
||||
* The function type for a listener is equivalent to:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(basic_registry<entity_type> &, entity_type);
|
||||
* @endcode
|
||||
*
|
||||
* Listeners are invoked **before** the object has been removed from the
|
||||
* entity.
|
||||
*
|
||||
* @sa sink
|
||||
*
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
[[nodiscard]] auto on_destroy() ENTT_NOEXCEPT {
|
||||
return sink{destruction};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns entities to a storage.
|
||||
* @tparam Args Types of arguments to use to construct the object.
|
||||
* @param owner The registry that issued the request.
|
||||
* @param entity A valid entity identifier.
|
||||
* @param args Parameters to use to initialize the object.
|
||||
* @return A reference to the newly created object.
|
||||
*/
|
||||
template<typename... Args>
|
||||
decltype(auto) emplace(basic_registry<entity_type> &owner, const entity_type entity, Args &&... args) {
|
||||
Type::emplace(entity, std::forward<Args>(args)...);
|
||||
construction.publish(owner, entity);
|
||||
return this->get(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns entities to a storage.
|
||||
* @tparam It Type of input iterator.
|
||||
* @tparam Args Types of arguments to use to construct the objects assigned
|
||||
* to the entities.
|
||||
* @param owner The registry that issued the request.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
* @param args Parameters to use to initialize the objects assigned to the
|
||||
* entities.
|
||||
*/
|
||||
template<typename It, typename... Args>
|
||||
void insert(basic_registry<entity_type> &owner, It first, It last, Args &&... args) {
|
||||
Type::insert(first, last, std::forward<Args>(args)...);
|
||||
|
||||
if(!construction.empty()) {
|
||||
for(; first != last; ++first) {
|
||||
construction.publish(owner, *first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Patches the given instance for an entity.
|
||||
* @tparam Func Types of the function objects to invoke.
|
||||
* @param owner The registry that issued the request.
|
||||
* @param entity A valid entity identifier.
|
||||
* @param func Valid function objects.
|
||||
* @return A reference to the patched instance.
|
||||
*/
|
||||
template<typename... Func>
|
||||
decltype(auto) patch(basic_registry<entity_type> &owner, const entity_type entity, Func &&... func) {
|
||||
Type::patch(entity, std::forward<Func>(func)...);
|
||||
update.publish(owner, entity);
|
||||
return this->get(entity);
|
||||
}
|
||||
|
||||
private:
|
||||
sigh<void(basic_registry<entity_type> &, const entity_type)> construction{};
|
||||
sigh<void(basic_registry<entity_type> &, const entity_type)> destruction{};
|
||||
sigh<void(basic_registry<entity_type> &, const entity_type)> update{};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Defines the component-to-storage conversion.
|
||||
*
|
||||
* 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 storage_traits {
|
||||
/*! @brief Resulting type after component-to-storage conversion. */
|
||||
using storage_type = sigh_storage_mixin<basic_storage<Entity, Type>>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Gets the element assigned to an entity from a storage, if any.
|
||||
* @tparam Type Storage type.
|
||||
* @param container A valid instance of a storage class.
|
||||
* @param entity A valid entity identifier.
|
||||
* @return A possibly empty tuple containing the requested element.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] auto get_as_tuple([[maybe_unused]] Type &container, [[maybe_unused]] const typename Type::entity_type entity) {
|
||||
static_assert(std::is_same_v<std::remove_const_t<Type>, typename storage_traits<typename Type::entity_type, typename Type::value_type>::storage_type>, "Invalid storage");
|
||||
|
||||
if constexpr(std::is_void_v<decltype(container.get({}))>) {
|
||||
return std::make_tuple();
|
||||
} else {
|
||||
return std::forward_as_tuple(container.get(entity));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
46
src/entt/entity/utility.hpp
Normal file
46
src/entt/entity/utility.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef ENTT_ENTITY_UTILITY_HPP
|
||||
#define ENTT_ENTITY_UTILITY_HPP
|
||||
|
||||
|
||||
#include "../core/type_traits.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Alias for exclusion lists.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct exclude_t: type_list<Type...> {};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Variable template for exclusion lists.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
inline constexpr exclude_t<Type...> exclude{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Alias for lists of observed components.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct get_t: type_list<Type...>{};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Variable template for lists of observed components.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
inline constexpr get_t<Type...> get{};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,50 @@
|
||||
#include "config/version.h"
|
||||
#include "core/algorithm.hpp"
|
||||
#include "core/any.hpp"
|
||||
#include "core/attribute.h"
|
||||
#include "core/family.hpp"
|
||||
#include "core/hashed_string.hpp"
|
||||
#include "core/ident.hpp"
|
||||
#include "entity/actor.hpp"
|
||||
#include "entity/entt_traits.hpp"
|
||||
#include "core/monostate.hpp"
|
||||
#include "core/type_info.hpp"
|
||||
#include "core/type_traits.hpp"
|
||||
#include "core/utility.hpp"
|
||||
#include "entity/entity.hpp"
|
||||
#include "entity/group.hpp"
|
||||
#include "entity/handle.hpp"
|
||||
#include "entity/helper.hpp"
|
||||
#include "entity/observer.hpp"
|
||||
#include "entity/organizer.hpp"
|
||||
#include "entity/poly_storage.hpp"
|
||||
#include "entity/registry.hpp"
|
||||
#include "entity/runtime_view.hpp"
|
||||
#include "entity/snapshot.hpp"
|
||||
#include "entity/sparse_set.hpp"
|
||||
#include "entity/storage.hpp"
|
||||
#include "entity/utility.hpp"
|
||||
#include "entity/view.hpp"
|
||||
#include "locator/locator.hpp"
|
||||
#include "meta/adl_pointer.hpp"
|
||||
#include "meta/container.hpp"
|
||||
#include "meta/ctx.hpp"
|
||||
#include "meta/factory.hpp"
|
||||
#include "meta/meta.hpp"
|
||||
#include "meta/node.hpp"
|
||||
#include "meta/pointer.hpp"
|
||||
#include "meta/policy.hpp"
|
||||
#include "meta/range.hpp"
|
||||
#include "meta/resolve.hpp"
|
||||
#include "meta/template.hpp"
|
||||
#include "meta/type_traits.hpp"
|
||||
#include "meta/utility.hpp"
|
||||
#include "platform/android-ndk-r17.hpp"
|
||||
#include "poly/poly.hpp"
|
||||
#include "process/process.hpp"
|
||||
#include "process/scheduler.hpp"
|
||||
#include "resource/cache.hpp"
|
||||
#include "resource/handle.hpp"
|
||||
#include "resource/loader.hpp"
|
||||
#include "signal/bus.hpp"
|
||||
#include "signal/delegate.hpp"
|
||||
#include "signal/dispatcher.hpp"
|
||||
#include "signal/emitter.hpp"
|
||||
#include "signal/sigh.hpp"
|
||||
#include "signal/signal.hpp"
|
||||
|
||||
5
src/entt/fwd.hpp
Normal file
5
src/entt/fwd.hpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "core/fwd.hpp"
|
||||
#include "entity/fwd.hpp"
|
||||
#include "poly/fwd.hpp"
|
||||
#include "resource/fwd.hpp"
|
||||
#include "signal/fwd.hpp"
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <cassert>
|
||||
#include "../config/config.h"
|
||||
|
||||
|
||||
namespace entt {
|
||||
@@ -14,7 +14,7 @@ namespace entt {
|
||||
* @brief Service locator, nothing more.
|
||||
*
|
||||
* A service locator can be used to do what it promises: locate services.<br/>
|
||||
* Usually service locators are tighly bound to the services they expose and
|
||||
* Usually service locators are tightly bound to the services they expose and
|
||||
* thus it's hard to define a general purpose class to do that. This template
|
||||
* based implementation tries to fill the gap and to get rid of the burden of
|
||||
* defining a different specific locator for each application.
|
||||
@@ -22,20 +22,20 @@ namespace entt {
|
||||
* @tparam Service Type of service managed by the locator.
|
||||
*/
|
||||
template<typename Service>
|
||||
struct ServiceLocator final {
|
||||
struct service_locator {
|
||||
/*! @brief Type of service offered. */
|
||||
using service_type = Service;
|
||||
|
||||
/*! @brief Default constructor, deleted on purpose. */
|
||||
ServiceLocator() = delete;
|
||||
service_locator() = delete;
|
||||
/*! @brief Default destructor, deleted on purpose. */
|
||||
~ServiceLocator() = delete;
|
||||
~service_locator() = delete;
|
||||
|
||||
/**
|
||||
* @brief Tests if a valid service implementation is set.
|
||||
* @return True if the service is set, false otherwise.
|
||||
*/
|
||||
inline static bool empty() noexcept {
|
||||
[[nodiscard]] static bool empty() ENTT_NOEXCEPT {
|
||||
return !static_cast<bool>(service);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ struct ServiceLocator final {
|
||||
*
|
||||
* @return A reference to the service implementation currently set, if any.
|
||||
*/
|
||||
inline static std::weak_ptr<Service> get() noexcept {
|
||||
[[nodiscard]] static std::weak_ptr<Service> get() ENTT_NOEXCEPT {
|
||||
return service;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ struct ServiceLocator final {
|
||||
*
|
||||
* @return A reference to the service implementation currently set, if any.
|
||||
*/
|
||||
inline static Service & ref() noexcept {
|
||||
[[nodiscard]] static Service & ref() ENTT_NOEXCEPT {
|
||||
return *service;
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ struct ServiceLocator final {
|
||||
* @param args Parameters to use to construct the service.
|
||||
*/
|
||||
template<typename Impl = Service, typename... Args>
|
||||
inline static void set(Args&&... args) {
|
||||
static void set(Args &&... args) {
|
||||
service = std::make_shared<Impl>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -86,8 +86,8 @@ struct ServiceLocator final {
|
||||
* @brief Sets or replaces a service.
|
||||
* @param ptr Service to use to replace the current one.
|
||||
*/
|
||||
inline static void set(std::shared_ptr<Service> ptr) {
|
||||
assert(static_cast<bool>(ptr));
|
||||
static void set(std::shared_ptr<Service> ptr) {
|
||||
ENTT_ASSERT(static_cast<bool>(ptr));
|
||||
service = std::move(ptr);
|
||||
}
|
||||
|
||||
@@ -96,20 +96,16 @@ struct ServiceLocator final {
|
||||
*
|
||||
* The service is no longer valid after a reset.
|
||||
*/
|
||||
inline static void reset() {
|
||||
static void reset() {
|
||||
service.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
static std::shared_ptr<Service> service;
|
||||
inline static std::shared_ptr<Service> service = nullptr;
|
||||
};
|
||||
|
||||
|
||||
template<typename Service>
|
||||
std::shared_ptr<Service> ServiceLocator<Service>::service{};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_LOCATOR_LOCATOR_HPP
|
||||
#endif
|
||||
|
||||
40
src/entt/meta/adl_pointer.hpp
Normal file
40
src/entt/meta/adl_pointer.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef ENTT_META_ADL_POINTER_HPP
|
||||
#define ENTT_META_ADL_POINTER_HPP
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief ADL based lookup function for dereferencing meta pointer-like types.
|
||||
* @tparam Type Element type.
|
||||
* @param value A pointer-like object.
|
||||
* @return The value returned from the dereferenced pointer.
|
||||
*/
|
||||
template<typename Type>
|
||||
decltype(auto) dereference_meta_pointer_like(const Type &value) {
|
||||
return *value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Fake ADL based lookup function for meta pointer-like types.
|
||||
* @tparam Type Element type.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct adl_meta_pointer_like {
|
||||
/**
|
||||
* @brief Uses the default ADL based lookup method to resolve the call.
|
||||
* @param value A pointer-like object.
|
||||
* @return The value returned from the dereferenced pointer.
|
||||
*/
|
||||
static decltype(auto) dereference(const Type &value) {
|
||||
return dereference_meta_pointer_like(value);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
406
src/entt/meta/container.hpp
Normal file
406
src/entt/meta/container.hpp
Normal file
@@ -0,0 +1,406 @@
|
||||
#ifndef ENTT_META_CONTAINER_HPP
|
||||
#define ENTT_META_CONTAINER_HPP
|
||||
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "type_traits.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Container traits.
|
||||
* @tparam Container Type of the underlying container.
|
||||
* @tparam Trait Traits associated with the underlying container.
|
||||
*/
|
||||
template<typename Container, template<typename> class... Trait>
|
||||
struct meta_container_traits: public Trait<Container>... {
|
||||
/*! @brief Type of container. */
|
||||
using type = Container;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Basic STL-compatible container traits
|
||||
* @tparam Container The type of the container.
|
||||
*/
|
||||
template<typename Container>
|
||||
struct basic_container {
|
||||
/**
|
||||
* @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 typename Container::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 typename Container::iterator begin(Container &cont) {
|
||||
return cont.begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 typename Container::const_iterator cbegin(const 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 typename Container::iterator end(Container &cont) {
|
||||
return cont.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 typename Container::const_iterator cend(const 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 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 typename Container::key_type &key) {
|
||||
return cont.find(key);
|
||||
}
|
||||
|
||||
/*! @copydoc find */
|
||||
[[nodiscard]] static typename Container::const_iterator cfind(const Container &cont, const typename Container::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([[maybe_unused]] 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([[maybe_unused]] Container &cont, [[maybe_unused]] 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::reference get(Container &cont, typename Container::size_type pos) {
|
||||
return cont[pos];
|
||||
}
|
||||
|
||||
/*! @copydoc get */
|
||||
[[nodiscard]] static typename Container::const_reference cget(const 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([[maybe_unused]] Container &cont, [[maybe_unused]] 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([[maybe_unused]] Container &cont, [[maybe_unused]] const typename Container::key_type &key, [[maybe_unused]] 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([[maybe_unused]] Container &cont, [[maybe_unused]] 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([[maybe_unused]] Container &cont, [[maybe_unused]] typename Container::const_iterator it, [[maybe_unused]] 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([[maybe_unused]] Container &cont, [[maybe_unused]] typename Container::const_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::const_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::const_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...>>
|
||||
: meta_container_traits<
|
||||
std::vector<Type, Args...>,
|
||||
basic_container,
|
||||
basic_dynamic_container,
|
||||
basic_sequence_container,
|
||||
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>>
|
||||
: meta_container_traits<
|
||||
std::array<Type, N>,
|
||||
basic_container,
|
||||
basic_sequence_container,
|
||||
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...>>
|
||||
: meta_container_traits<
|
||||
std::map<Key, Value, Args...>,
|
||||
basic_container,
|
||||
basic_associative_container,
|
||||
basic_dynamic_container,
|
||||
basic_dynamic_associative_container,
|
||||
dynamic_associative_key_value_container
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
/**
|
||||
* @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...>>
|
||||
: meta_container_traits<
|
||||
std::unordered_map<Key, Value, Args...>,
|
||||
basic_container,
|
||||
basic_associative_container,
|
||||
basic_dynamic_container,
|
||||
basic_dynamic_associative_container,
|
||||
dynamic_associative_key_value_container
|
||||
>
|
||||
{};
|
||||
|
||||
|
||||
/**
|
||||
* @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...>>
|
||||
: meta_container_traits<
|
||||
std::set<Key, Args...>,
|
||||
basic_container,
|
||||
basic_associative_container,
|
||||
basic_dynamic_container,
|
||||
basic_dynamic_associative_container,
|
||||
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...>>
|
||||
: meta_container_traits<
|
||||
std::unordered_set<Key, Args...>,
|
||||
basic_container,
|
||||
basic_associative_container,
|
||||
basic_dynamic_container,
|
||||
basic_dynamic_associative_container,
|
||||
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
|
||||
574
src/entt/meta/factory.hpp
Normal file
574
src/entt/meta/factory.hpp
Normal file
@@ -0,0 +1,574 @@
|
||||
#ifndef ENTT_META_FACTORY_HPP
|
||||
#define ENTT_META_FACTORY_HPP
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "meta.hpp"
|
||||
#include "node.hpp"
|
||||
#include "policy.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
|
||||
namespace internal {
|
||||
|
||||
|
||||
template<typename Node>
|
||||
[[nodiscard]] bool find_if(const Node *candidate, const Node *node) ENTT_NOEXCEPT {
|
||||
return node && (node == candidate || find_if(candidate, node->next));
|
||||
}
|
||||
|
||||
|
||||
template<typename Id, typename Node>
|
||||
[[nodiscard]] bool find_if_not(const Id id, Node *node, const Node *owner) ENTT_NOEXCEPT {
|
||||
if constexpr(std::is_pointer_v<Id>) {
|
||||
return node && ((*node->id == *id && node != owner) || find_if_not(id, node->next, owner));
|
||||
} else {
|
||||
return node && ((node->id == id && node != owner) || find_if_not(id, node->next, owner));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief Meta factory to be used for reflection purposes.
|
||||
*
|
||||
* The meta factory is an utility class used to reflect types, data members and
|
||||
* functions of all sorts. This class ensures that the underlying web of types
|
||||
* is built correctly and performs some checks in debug mode to ensure that
|
||||
* there are no subtle errors at runtime.
|
||||
*/
|
||||
template<typename...>
|
||||
struct meta_factory;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Extended meta factory to be used for reflection purposes.
|
||||
* @tparam Type Reflected type for which the factory was created.
|
||||
* @tparam Spec Property specialization pack used to disambiguate overloads.
|
||||
*/
|
||||
template<typename Type, typename... Spec>
|
||||
struct meta_factory<Type, Spec...>: public meta_factory<Type> {
|
||||
private:
|
||||
template<std::size_t Step = 0, std::size_t... Index, typename... Property, typename... Other>
|
||||
void unpack(std::index_sequence<Index...>, std::tuple<Property...> property, Other &&... other) {
|
||||
unroll<Step>(choice<3>, std::move(std::get<Index>(property))..., std::forward<Other>(other)...);
|
||||
}
|
||||
|
||||
template<std::size_t Step = 0, typename... Property, typename... Other>
|
||||
void unroll(choice_t<3>, std::tuple<Property...> property, Other &&... other) {
|
||||
unpack<Step>(std::index_sequence_for<Property...>{}, std::move(property), std::forward<Other>(other)...);
|
||||
}
|
||||
|
||||
template<std::size_t Step = 0, typename... Property, typename... Other>
|
||||
void unroll(choice_t<2>, std::pair<Property...> property, Other &&... other) {
|
||||
assign<Step>(std::move(property.first), std::move(property.second));
|
||||
unroll<Step+1>(choice<3>, std::forward<Other>(other)...);
|
||||
}
|
||||
|
||||
template<std::size_t Step = 0, typename Property, typename... Other>
|
||||
std::enable_if_t<!std::is_invocable_v<Property>>
|
||||
unroll(choice_t<1>, Property &&property, Other &&... other) {
|
||||
assign<Step>(std::forward<Property>(property));
|
||||
unroll<Step+1>(choice<3>, std::forward<Other>(other)...);
|
||||
}
|
||||
|
||||
template<std::size_t Step = 0, typename Func, typename... Other>
|
||||
void unroll(choice_t<0>, Func &&invocable, Other &&... other) {
|
||||
unroll<Step>(choice<3>, std::forward<Func>(invocable)(), std::forward<Other>(other)...);
|
||||
}
|
||||
|
||||
template<std::size_t>
|
||||
void unroll(choice_t<0>) {}
|
||||
|
||||
template<std::size_t = 0, typename Key>
|
||||
void assign(Key &&key, meta_any value = {}) {
|
||||
static meta_any property[2u]{};
|
||||
|
||||
static internal::meta_prop_node node{
|
||||
nullptr,
|
||||
property,
|
||||
property + 1u
|
||||
};
|
||||
|
||||
entt::meta_any instance{std::forward<Key>(key)};
|
||||
ENTT_ASSERT(!internal::find_if_not(&instance, *curr, &node));
|
||||
property[0u] = std::move(instance);
|
||||
property[1u] = std::move(value);
|
||||
|
||||
if(!internal::find_if(&node, *curr)) {
|
||||
node.next = *curr;
|
||||
*curr = &node;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs an extended factory from a given node.
|
||||
* @param target The underlying node to which to assign the properties.
|
||||
*/
|
||||
meta_factory(internal::meta_prop_node **target) ENTT_NOEXCEPT
|
||||
: curr{target}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Assigns a property to the last meta object created.
|
||||
*
|
||||
* Both the key and the value (if any) must be at least copy constructible.
|
||||
*
|
||||
* @tparam PropertyOrKey Type of the property or property key.
|
||||
* @tparam Value Optional type of the property value.
|
||||
* @param property_or_key Property or property key.
|
||||
* @param value Optional property value.
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<typename PropertyOrKey, typename... Value>
|
||||
auto prop(PropertyOrKey &&property_or_key, Value &&... value) && {
|
||||
if constexpr(sizeof...(Value) == 0) {
|
||||
unroll(choice<3>, std::forward<PropertyOrKey>(property_or_key));
|
||||
} else {
|
||||
assign(std::forward<PropertyOrKey>(property_or_key), std::forward<Value>(value)...);
|
||||
}
|
||||
|
||||
return meta_factory<Type, Spec..., PropertyOrKey, Value...>{curr};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns properties to the last meta object created.
|
||||
*
|
||||
* Both the keys and the values (if any) must be at least copy
|
||||
* constructible.
|
||||
*
|
||||
* @tparam Property Types of the properties.
|
||||
* @param property Properties to assign to the last meta object created.
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template <typename... Property>
|
||||
auto props(Property... property) && {
|
||||
unroll(choice<3>, std::forward<Property>(property)...);
|
||||
return meta_factory<Type, Spec..., Property...>{curr};
|
||||
}
|
||||
|
||||
private:
|
||||
internal::meta_prop_node **curr;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Basic meta factory to be used for reflection purposes.
|
||||
* @tparam Type Reflected type for which the factory was created.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct meta_factory<Type> {
|
||||
/**
|
||||
* @brief Makes a meta type _searchable_.
|
||||
* @param id Optional unique identifier.
|
||||
* @return An extended meta factory for the given type.
|
||||
*/
|
||||
auto type(const id_type id = type_hash<Type>::value()) {
|
||||
auto * const node = internal::meta_info<Type>::resolve();
|
||||
|
||||
ENTT_ASSERT(!internal::find_if_not(id, *internal::meta_context::global(), node));
|
||||
node->id = id;
|
||||
|
||||
if(!internal::find_if(node, *internal::meta_context::global())) {
|
||||
node->next = *internal::meta_context::global();
|
||||
*internal::meta_context::global() = node;
|
||||
}
|
||||
|
||||
return meta_factory<Type, Type>{&node->prop};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a meta base to a meta type.
|
||||
*
|
||||
* A reflected base class must be a real base class of the reflected type.
|
||||
*
|
||||
* @tparam Base Type of the base class to assign to the meta type.
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<typename Base>
|
||||
auto base() ENTT_NOEXCEPT {
|
||||
static_assert(std::is_base_of_v<Base, Type>, "Invalid base type");
|
||||
auto * const type = internal::meta_info<Type>::resolve();
|
||||
|
||||
static internal::meta_base_node node{
|
||||
type,
|
||||
nullptr,
|
||||
&internal::meta_info<Base>::resolve,
|
||||
[](const void *instance) ENTT_NOEXCEPT -> const void * {
|
||||
return static_cast<const Base *>(static_cast<const Type *>(instance));
|
||||
}
|
||||
};
|
||||
|
||||
if(!internal::find_if(&node, type->base)) {
|
||||
node.next = type->base;
|
||||
type->base = &node;
|
||||
}
|
||||
|
||||
return meta_factory<Type>{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a meta conversion function to a meta type.
|
||||
*
|
||||
* The given type must be such that an instance of the reflected type can be
|
||||
* converted to it.
|
||||
*
|
||||
* @tparam To Type of the conversion function to assign to the meta type.
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<typename To>
|
||||
auto conv() ENTT_NOEXCEPT {
|
||||
static_assert(std::is_convertible_v<Type, To>, "Could not convert to the required type");
|
||||
auto * const type = internal::meta_info<Type>::resolve();
|
||||
|
||||
static internal::meta_conv_node node{
|
||||
type,
|
||||
nullptr,
|
||||
&internal::meta_info<To>::resolve,
|
||||
[](const void *instance) -> meta_any {
|
||||
return static_cast<To>(*static_cast<const Type *>(instance));
|
||||
}
|
||||
};
|
||||
|
||||
if(!internal::find_if(&node, type->conv)) {
|
||||
node.next = type->conv;
|
||||
type->conv = &node;
|
||||
}
|
||||
|
||||
return meta_factory<Type>{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a meta conversion function to a meta type.
|
||||
*
|
||||
* Conversion functions can be either free functions or member
|
||||
* functions.<br/>
|
||||
* In case of free functions, they must accept a const reference to an
|
||||
* instance of the parent type as an argument. In case of member functions,
|
||||
* they should have no arguments at all.
|
||||
*
|
||||
* @tparam Candidate The actual function to use for the conversion.
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<auto Candidate>
|
||||
auto conv() ENTT_NOEXCEPT {
|
||||
using conv_type = std::invoke_result_t<decltype(Candidate), Type &>;
|
||||
auto * const type = internal::meta_info<Type>::resolve();
|
||||
|
||||
static internal::meta_conv_node node{
|
||||
type,
|
||||
nullptr,
|
||||
&internal::meta_info<conv_type>::resolve,
|
||||
[](const void *instance) -> meta_any {
|
||||
return std::invoke(Candidate, *static_cast<const Type *>(instance));
|
||||
}
|
||||
};
|
||||
|
||||
if(!internal::find_if(&node, type->conv)) {
|
||||
node.next = type->conv;
|
||||
type->conv = &node;
|
||||
}
|
||||
|
||||
return meta_factory<Type>{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a meta constructor to a meta type.
|
||||
*
|
||||
* Both member functions and free function can be assigned to meta types in
|
||||
* the role of constructors. All that is required is that they return an
|
||||
* instance of the underlying type.<br/>
|
||||
* From a client's point of view, nothing changes if a constructor of a meta
|
||||
* type is a built-in one or not.
|
||||
*
|
||||
* @tparam Candidate The actual function to use as a constructor.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @return An extended meta factory for the parent type.
|
||||
*/
|
||||
template<auto Candidate, typename Policy = as_is_t>
|
||||
auto ctor() ENTT_NOEXCEPT {
|
||||
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
|
||||
static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>, Type>, "The function doesn't return an object of the required type");
|
||||
auto * const type = internal::meta_info<Type>::resolve();
|
||||
|
||||
static internal::meta_ctor_node node{
|
||||
type,
|
||||
nullptr,
|
||||
nullptr,
|
||||
descriptor::args_type::size,
|
||||
[](const typename internal::meta_ctor_node::size_type index) ENTT_NOEXCEPT {
|
||||
return meta_arg(typename descriptor::args_type{}, index);
|
||||
},
|
||||
[](meta_any * const args) {
|
||||
return meta_invoke<Type, Candidate, Policy>({}, args, std::make_index_sequence<descriptor::args_type::size>{});
|
||||
}
|
||||
};
|
||||
|
||||
if(!internal::find_if(&node, type->ctor)) {
|
||||
node.next = type->ctor;
|
||||
type->ctor = &node;
|
||||
}
|
||||
|
||||
return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a meta constructor to a meta type.
|
||||
*
|
||||
* A meta constructor is uniquely identified by the types of its arguments
|
||||
* and is such that there exists an actual constructor of the underlying
|
||||
* type that can be invoked with parameters whose types are those given.
|
||||
*
|
||||
* @tparam Args Types of arguments to use to construct an instance.
|
||||
* @return An extended meta factory for the parent type.
|
||||
*/
|
||||
template<typename... Args>
|
||||
auto ctor() ENTT_NOEXCEPT {
|
||||
using descriptor = meta_function_helper_t<Type, Type(*)(Args...)>;
|
||||
auto * const type = internal::meta_info<Type>::resolve();
|
||||
|
||||
static internal::meta_ctor_node node{
|
||||
type,
|
||||
nullptr,
|
||||
nullptr,
|
||||
descriptor::args_type::size,
|
||||
[](const typename internal::meta_ctor_node::size_type index) ENTT_NOEXCEPT {
|
||||
return meta_arg(typename descriptor::args_type{}, index);
|
||||
},
|
||||
[](meta_any * const args) {
|
||||
return meta_construct<Type, Args...>(args, std::make_index_sequence<descriptor::args_type::size>{});
|
||||
}
|
||||
};
|
||||
|
||||
if(!internal::find_if(&node, type->ctor)) {
|
||||
node.next = type->ctor;
|
||||
type->ctor = &node;
|
||||
}
|
||||
|
||||
return meta_factory<Type, Type(Args...)>{&node.prop};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a meta destructor to a meta type.
|
||||
*
|
||||
* Free functions can be assigned to meta types in the role of destructors.
|
||||
* The signature of the function should identical to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(Type &);
|
||||
* @endcode
|
||||
*
|
||||
* The purpose is to give users the ability to free up resources that
|
||||
* require special treatment before an object is actually destroyed.
|
||||
*
|
||||
* @tparam Func The actual function to use as a destructor.
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<auto Func>
|
||||
auto dtor() ENTT_NOEXCEPT {
|
||||
static_assert(std::is_invocable_v<decltype(Func), Type &>, "The function doesn't accept an object of the type provided");
|
||||
auto * const type = internal::meta_info<Type>::resolve();
|
||||
|
||||
type->dtor = [](void *instance) {
|
||||
std::invoke(Func, *static_cast<Type *>(instance));
|
||||
};
|
||||
|
||||
return meta_factory<Type>{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a meta data to a meta type.
|
||||
*
|
||||
* Both data members and static and global variables, as well as constants
|
||||
* of any kind, can be assigned to a meta type.<br/>
|
||||
* From a client's point of view, all the variables associated with the
|
||||
* reflected object will appear as if they were part of the type itself.
|
||||
*
|
||||
* @tparam Data The actual variable to attach to the meta type.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param id Unique identifier.
|
||||
* @return An extended meta factory for the parent type.
|
||||
*/
|
||||
template<auto Data, typename Policy = as_is_t>
|
||||
auto data(const id_type id) ENTT_NOEXCEPT {
|
||||
if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
|
||||
return data<Data, Data, Policy>(id);
|
||||
} else {
|
||||
using data_type = std::remove_pointer_t<decltype(Data)>;
|
||||
auto * const type = internal::meta_info<Type>::resolve();
|
||||
|
||||
static internal::meta_data_node node{
|
||||
{},
|
||||
type,
|
||||
nullptr,
|
||||
nullptr,
|
||||
std::is_same_v<Type, data_type> || std::is_const_v<data_type>,
|
||||
true,
|
||||
&internal::meta_info<data_type>::resolve,
|
||||
&meta_setter<Type, Data>,
|
||||
&meta_getter<Type, Data, Policy>
|
||||
};
|
||||
|
||||
ENTT_ASSERT(!internal::find_if_not(id, type->data, &node));
|
||||
node.id = id;
|
||||
|
||||
if(!internal::find_if(&node, type->data)) {
|
||||
node.next = type->data;
|
||||
type->data = &node;
|
||||
}
|
||||
|
||||
return meta_factory<Type, std::integral_constant<decltype(Data), Data>>{&node.prop};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a meta data to a meta type by means of its setter and
|
||||
* getter.
|
||||
*
|
||||
* Setters and getters can be either free functions, member functions or a
|
||||
* mix of them.<br/>
|
||||
* In case of free functions, setters and getters must accept a reference to
|
||||
* an instance of the parent type as their first argument. A setter has then
|
||||
* an extra argument of a type convertible to that of the parameter to
|
||||
* set.<br/>
|
||||
* In case of member functions, getters have no arguments at all, while
|
||||
* setters has an argument of a type convertible to that of the parameter to
|
||||
* set.
|
||||
*
|
||||
* @tparam Setter The actual function to use as a setter.
|
||||
* @tparam Getter The actual function to use as a getter.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param id Unique identifier.
|
||||
* @return An extended meta factory for the parent type.
|
||||
*/
|
||||
template<auto Setter, auto Getter, typename Policy = as_is_t>
|
||||
auto data(const id_type id) ENTT_NOEXCEPT {
|
||||
using underlying_type = std::remove_reference_t<std::invoke_result_t<decltype(Getter), Type &>>;
|
||||
auto * const type = internal::meta_info<Type>::resolve();
|
||||
|
||||
static internal::meta_data_node node{
|
||||
{},
|
||||
type,
|
||||
nullptr,
|
||||
nullptr,
|
||||
std::is_same_v<decltype(Setter), std::nullptr_t> || (std::is_member_object_pointer_v<decltype(Setter)> && std::is_const_v<underlying_type>),
|
||||
false,
|
||||
&internal::meta_info<underlying_type>::resolve,
|
||||
&meta_setter<Type, Setter>,
|
||||
&meta_getter<Type, Getter, Policy>
|
||||
};
|
||||
|
||||
ENTT_ASSERT(!internal::find_if_not(id, type->data, &node));
|
||||
node.id = id;
|
||||
|
||||
if(!internal::find_if(&node, type->data)) {
|
||||
node.next = type->data;
|
||||
type->data = &node;
|
||||
}
|
||||
|
||||
return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a meta funcion to a meta type.
|
||||
*
|
||||
* Both member functions and free functions can be assigned to a meta
|
||||
* type.<br/>
|
||||
* From a client's point of view, all the functions associated with the
|
||||
* reflected object will appear as if they were part of the type itself.
|
||||
*
|
||||
* @tparam Candidate The actual function to attach to the meta type.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param id Unique identifier.
|
||||
* @return An extended meta factory for the parent type.
|
||||
*/
|
||||
template<auto Candidate, typename Policy = as_is_t>
|
||||
auto func(const id_type id) ENTT_NOEXCEPT {
|
||||
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
|
||||
auto * const type = internal::meta_info<Type>::resolve();
|
||||
|
||||
static internal::meta_func_node node{
|
||||
{},
|
||||
type,
|
||||
nullptr,
|
||||
nullptr,
|
||||
descriptor::args_type::size,
|
||||
descriptor::is_const,
|
||||
descriptor::is_static,
|
||||
&internal::meta_info<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, typename descriptor::return_type>>::resolve,
|
||||
[](const typename internal::meta_func_node::size_type index) ENTT_NOEXCEPT {
|
||||
return meta_arg(typename descriptor::args_type{}, index);
|
||||
},
|
||||
[](meta_handle instance, meta_any *args) {
|
||||
return meta_invoke<Type, Candidate, Policy>(std::move(instance), args, std::make_index_sequence<descriptor::args_type::size>{});
|
||||
}
|
||||
};
|
||||
|
||||
for(auto *it = &type->func; *it; it = &(*it)->next) {
|
||||
if(*it == &node) {
|
||||
*it = node.next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
internal::meta_func_node **it = &type->func;
|
||||
for(; *it && (*it)->id != id; it = &(*it)->next);
|
||||
for(; *it && (*it)->id == id && (*it)->arity < node.arity; it = &(*it)->next);
|
||||
|
||||
node.id = id;
|
||||
node.next = *it;
|
||||
*it = &node;
|
||||
|
||||
return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Utility function to use for reflection.
|
||||
*
|
||||
* This is the point from which everything starts.<br/>
|
||||
* By invoking this function with a type that is not yet reflected, a meta type
|
||||
* is created to which it will be possible to attach meta objects through a
|
||||
* dedicated factory.
|
||||
*
|
||||
* @tparam Type Type to reflect.
|
||||
* @return A meta factory for the given type.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] auto meta() ENTT_NOEXCEPT {
|
||||
auto * const node = internal::meta_info<Type>::resolve();
|
||||
// extended meta factory to allow assigning properties to opaque meta types
|
||||
return meta_factory<Type, Type>{&node->prop};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
2193
src/entt/meta/meta.hpp
Normal file
2193
src/entt/meta/meta.hpp
Normal file
File diff suppressed because it is too large
Load Diff
270
src/entt/meta/node.hpp
Normal file
270
src/entt/meta/node.hpp
Normal file
@@ -0,0 +1,270 @@
|
||||
#ifndef ENTT_META_NODE_HPP
|
||||
#define ENTT_META_NODE_HPP
|
||||
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/attribute.h"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "type_traits.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
class meta_any;
|
||||
class meta_type;
|
||||
struct meta_handle;
|
||||
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
|
||||
namespace internal {
|
||||
|
||||
|
||||
struct meta_type_node;
|
||||
|
||||
|
||||
struct meta_prop_node {
|
||||
meta_prop_node * next;
|
||||
const meta_any * const id;
|
||||
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 arity;
|
||||
meta_type(* const arg)(const 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_const;
|
||||
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 arity;
|
||||
const bool is_const;
|
||||
const bool is_static;
|
||||
meta_type_node *(* const ret)() ENTT_NOEXCEPT;
|
||||
meta_type(* const arg)(const size_type) ENTT_NOEXCEPT;
|
||||
meta_any(* const invoke)(meta_handle, meta_any *);
|
||||
};
|
||||
|
||||
|
||||
struct meta_template_info {
|
||||
using size_type = std::size_t;
|
||||
const bool is_template_specialization;
|
||||
const size_type arity;
|
||||
meta_type_node *(* const type)() ENTT_NOEXCEPT;
|
||||
meta_type_node *(* const arg)(const size_type) ENTT_NOEXCEPT;
|
||||
};
|
||||
|
||||
|
||||
struct meta_type_node {
|
||||
using size_type = std::size_t;
|
||||
const type_info info;
|
||||
id_type id;
|
||||
meta_type_node * next;
|
||||
meta_prop_node * prop;
|
||||
const size_type size_of;
|
||||
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 meta_template_info template_info;
|
||||
const size_type rank;
|
||||
size_type(* const extent)(const size_type) ENTT_NOEXCEPT ;
|
||||
meta_type_node *(* const remove_pointer)() ENTT_NOEXCEPT;
|
||||
meta_type_node *(* const remove_extent)() ENTT_NOEXCEPT;
|
||||
meta_ctor_node * const def_ctor;
|
||||
meta_ctor_node *ctor{nullptr};
|
||||
meta_base_node *base{nullptr};
|
||||
meta_conv_node *conv{nullptr};
|
||||
meta_data_node *data{nullptr};
|
||||
meta_func_node *func{nullptr};
|
||||
void(* dtor)(void *){nullptr};
|
||||
};
|
||||
|
||||
|
||||
template<auto Member, typename Op, typename Node>
|
||||
auto meta_visit(const Op &op, const Node *node)
|
||||
-> std::decay_t<decltype(node->*Member)> {
|
||||
for(auto *curr = node->*Member; curr; curr = curr->next) {
|
||||
if(op(curr)) {
|
||||
return curr;
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr(std::is_same_v<Node, meta_type_node>) {
|
||||
for(auto *curr = node->base; curr; curr = curr->next) {
|
||||
if(auto *ret = meta_visit<Member>(op, curr->type()); ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
template<typename... Args>
|
||||
meta_type_node * meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT;
|
||||
|
||||
|
||||
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");
|
||||
|
||||
template<std::size_t... Index>
|
||||
[[nodiscard]] static auto extent(const meta_type_node::size_type dim, std::index_sequence<Index...>) ENTT_NOEXCEPT {
|
||||
meta_type_node::size_type ext{};
|
||||
((ext = (dim == Index ? std::extent_v<Type, Index> : ext)), ...);
|
||||
return ext;
|
||||
}
|
||||
|
||||
[[nodiscard]] static meta_ctor_node * meta_default_constructor([[maybe_unused]] meta_type_node *type) ENTT_NOEXCEPT {
|
||||
if constexpr(std::is_default_constructible_v<Type>) {
|
||||
static meta_ctor_node node{
|
||||
type,
|
||||
nullptr,
|
||||
nullptr,
|
||||
0u,
|
||||
nullptr,
|
||||
[](meta_any * const) { return meta_any{std::in_place_type<Type>}; }
|
||||
};
|
||||
|
||||
return &node;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] static meta_template_info meta_template_descriptor() ENTT_NOEXCEPT {
|
||||
if constexpr(is_complete_v<meta_template_traits<Type>>) {
|
||||
return {
|
||||
true,
|
||||
meta_template_traits<Type>::args_type::size,
|
||||
&meta_node<typename meta_template_traits<Type>::class_type>::resolve,
|
||||
[](const std::size_t index) ENTT_NOEXCEPT {
|
||||
return meta_arg_node(typename meta_template_traits<Type>::args_type{}, index);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return { false, 0u, nullptr, nullptr };
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
[[nodiscard]] static meta_type_node * resolve() ENTT_NOEXCEPT {
|
||||
static meta_type_node node{
|
||||
type_id<Type>(),
|
||||
{},
|
||||
nullptr,
|
||||
nullptr,
|
||||
size_of_v<Type>,
|
||||
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>,
|
||||
is_complete_v<meta_sequence_container_traits<Type>>,
|
||||
is_complete_v<meta_associative_container_traits<Type>>,
|
||||
meta_template_descriptor(),
|
||||
std::rank_v<Type>,
|
||||
[](meta_type_node::size_type dim) ENTT_NOEXCEPT { return extent(dim, std::make_index_sequence<std::rank_v<Type>>{}); },
|
||||
&meta_node<std::remove_cv_t<std::remove_pointer_t<Type>>>::resolve,
|
||||
&meta_node<std::remove_cv_t<std::remove_reference_t<std::remove_extent_t<Type>>>>::resolve,
|
||||
meta_default_constructor(&node),
|
||||
meta_default_constructor(&node)
|
||||
};
|
||||
|
||||
return &node;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename Type>
|
||||
struct meta_info: meta_node<std::remove_cv_t<std::remove_reference_t<Type>>> {};
|
||||
|
||||
|
||||
template<typename... Args>
|
||||
meta_type_node * meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT {
|
||||
return std::array<meta_type_node *, sizeof...(Args)>{{internal::meta_info<Args>::resolve()...}}[index];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
49
src/entt/meta/pointer.hpp
Normal file
49
src/entt/meta/pointer.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#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
|
||||
27
src/entt/meta/policy.hpp
Normal file
27
src/entt/meta/policy.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef ENTT_META_POLICY_HPP
|
||||
#define ENTT_META_POLICY_HPP
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/*! @brief Empty class type used to request the _as ref_ policy. */
|
||||
struct as_ref_t {};
|
||||
|
||||
|
||||
/*! @brief Empty class type used to request the _as cref_ policy. */
|
||||
struct as_cref_t {};
|
||||
|
||||
|
||||
/*! @brief Empty class type used to request the _as-is_ policy. */
|
||||
struct as_is_t {};
|
||||
|
||||
|
||||
/*! @brief Empty class type used to request the _as void_ policy. */
|
||||
struct as_void_t {};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
100
src/entt/meta/range.hpp
Normal file
100
src/entt/meta/range.hpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifndef ENTT_META_RANGE_HPP
|
||||
#define ENTT_META_RANGE_HPP
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Iterable range to use to iterate all types of meta objects.
|
||||
* @tparam Type Type of meta objects returned.
|
||||
* @tparam Node Type of meta nodes iterated.
|
||||
*/
|
||||
template<typename Type, typename Node = typename Type::node_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 = Node;
|
||||
|
||||
range_iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
range_iterator(node_type *head) ENTT_NOEXCEPT
|
||||
: it{head}
|
||||
{}
|
||||
|
||||
range_iterator & operator++() ENTT_NOEXCEPT {
|
||||
return (it = it->next), *this;
|
||||
}
|
||||
|
||||
range_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
range_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
|
||||
return it;
|
||||
}
|
||||
|
||||
[[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:
|
||||
node_type *it{};
|
||||
};
|
||||
|
||||
public:
|
||||
/*! @brief Node type. */
|
||||
using node_type = Node;
|
||||
/*! @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
|
||||
72
src/entt/meta/resolve.hpp
Normal file
72
src/entt/meta/resolve.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef ENTT_META_RESOLVE_HPP
|
||||
#define ENTT_META_RESOLVE_HPP
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include "../core/type_info.hpp"
|
||||
#include "ctx.hpp"
|
||||
#include "meta.hpp"
|
||||
#include "node.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(const id_type id) ENTT_NOEXCEPT {
|
||||
for(auto *curr = *internal::meta_context::global(); curr; curr = curr->next) {
|
||||
if(curr->id == id) {
|
||||
return curr;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns the meta type associated with a given type info object, if
|
||||
* any.
|
||||
* @param info The type info object of the requested type.
|
||||
* @return The meta type associated with the given type info object, if any.
|
||||
*/
|
||||
[[nodiscard]] inline meta_type resolve(const type_info info) ENTT_NOEXCEPT {
|
||||
for(auto *curr = *internal::meta_context::global(); curr; curr = curr->next) {
|
||||
if(curr->info == info) {
|
||||
return curr;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
33
src/entt/meta/template.hpp
Normal file
33
src/entt/meta/template.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef ENTT_META_TEMPLATE_HPP
|
||||
#define ENTT_META_TEMPLATE_HPP
|
||||
|
||||
|
||||
#include "../core/type_traits.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/*! @brief Utility class to disambiguate class templates. */
|
||||
template<template<typename...> typename>
|
||||
struct meta_class_template_tag {};
|
||||
|
||||
|
||||
/**
|
||||
* @brief General purpose traits class for generating meta template information.
|
||||
* @tparam Clazz Type of class template.
|
||||
* @tparam Args Types of template arguments.
|
||||
*/
|
||||
template<template<typename...> typename Clazz, typename... Args>
|
||||
struct meta_template_traits<Clazz<Args...>> {
|
||||
/*! @brief Wrapped class template. */
|
||||
using class_type = meta_class_template_tag<Clazz>;
|
||||
/*! @brief List of template arguments. */
|
||||
using args_type = type_list<Args...>;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
88
src/entt/meta/type_traits.hpp
Normal file
88
src/entt/meta/type_traits.hpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#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
|
||||
* template information.
|
||||
*/
|
||||
template<typename>
|
||||
struct meta_template_traits;
|
||||
|
||||
|
||||
/**
|
||||
* @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 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>::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 Partial specialization to ensure that const pointer-like types are
|
||||
* also accepted.
|
||||
* @tparam Type Potentially pointer-like type.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct is_meta_pointer_like<const Type>: is_meta_pointer_like<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
|
||||
297
src/entt/meta/utility.hpp
Normal file
297
src/entt/meta/utility.hpp
Normal file
@@ -0,0 +1,297 @@
|
||||
#ifndef ENTT_META_UTILITY_HPP
|
||||
#define ENTT_META_UTILITY_HPP
|
||||
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "meta.hpp"
|
||||
#include "node.hpp"
|
||||
#include "policy.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename, typename>
|
||||
struct meta_function_descriptor;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Meta function descriptor.
|
||||
* @tparam Type Reflected type to which the meta function is associated.
|
||||
* @tparam Ret Function return type.
|
||||
* @tparam Class Actual owner of the member function.
|
||||
* @tparam Args Function arguments.
|
||||
*/
|
||||
template<typename Type, typename Ret, typename Class, typename... Args>
|
||||
struct meta_function_descriptor<Type, Ret(Class:: *)(Args...) const> {
|
||||
/*! @brief Meta function return type. */
|
||||
using return_type = Ret;
|
||||
/*! @brief Meta function arguments. */
|
||||
using args_type = std::conditional_t<std::is_same_v<Type, Class>, type_list<Args...>, type_list<const Class &, Args...>>;
|
||||
|
||||
/*! @brief True if the meta function is const, false otherwise. */
|
||||
static constexpr auto is_const = true;
|
||||
/*! @brief True if the meta function is static, false otherwise. */
|
||||
static constexpr auto is_static = !std::is_same_v<Type, Class>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Meta function descriptor.
|
||||
* @tparam Type Reflected type to which the meta function is associated.
|
||||
* @tparam Ret Function return type.
|
||||
* @tparam Class Actual owner of the member function.
|
||||
* @tparam Args Function arguments.
|
||||
*/
|
||||
template<typename Type, typename Ret, typename Class, typename... Args>
|
||||
struct meta_function_descriptor<Type, Ret(Class:: *)(Args...)> {
|
||||
/*! @brief Meta function return type. */
|
||||
using return_type = Ret;
|
||||
/*! @brief Meta function arguments. */
|
||||
using args_type = std::conditional_t<std::is_same_v<Type, Class>, type_list<Args...>, type_list<Class &, Args...>>;
|
||||
|
||||
/*! @brief True if the meta function is const, false otherwise. */
|
||||
static constexpr auto is_const = false;
|
||||
/*! @brief True if the meta function is static, false otherwise. */
|
||||
static constexpr auto is_static = !std::is_same_v<Type, Class>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Meta function descriptor.
|
||||
* @tparam Type Reflected type to which the meta function is associated.
|
||||
* @tparam Ret Function return type.
|
||||
* @tparam Args Function arguments.
|
||||
*/
|
||||
template<typename Type, typename Ret, typename... Args>
|
||||
struct meta_function_descriptor<Type, Ret(*)(Args...)> {
|
||||
/*! @brief Meta function return type. */
|
||||
using return_type = Ret;
|
||||
/*! @brief Meta function arguments. */
|
||||
using args_type = type_list<Args...>;
|
||||
|
||||
/*! @brief True if the meta function is const, false otherwise. */
|
||||
static constexpr auto is_const = false;
|
||||
/*! @brief True if the meta function is static, false otherwise. */
|
||||
static constexpr auto is_static = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Meta function helper.
|
||||
*
|
||||
* Converts a function type to be associated with a reflected type into its meta
|
||||
* function descriptor.
|
||||
*
|
||||
* @tparam Type Reflected type to which the meta function is associated.
|
||||
* @tparam Candidate The actual function to associate with the reflected type.
|
||||
*/
|
||||
template<typename Type, typename Candidate>
|
||||
class meta_function_helper {
|
||||
template<typename Ret, typename... Args, typename Class>
|
||||
static constexpr meta_function_descriptor<Type, Ret(Class:: *)(Args...) const> get_rid_of_noexcept(Ret(Class:: *)(Args...) const);
|
||||
|
||||
template<typename Ret, typename... Args, typename Class>
|
||||
static constexpr meta_function_descriptor<Type, Ret(Class:: *)(Args...)> get_rid_of_noexcept(Ret(Class:: *)(Args...));
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
static constexpr meta_function_descriptor<Type, Ret(*)(Args...)> get_rid_of_noexcept(Ret(*)(Args...));
|
||||
|
||||
public:
|
||||
/*! @brief The meta function descriptor of the given function. */
|
||||
using type = decltype(get_rid_of_noexcept(std::declval<Candidate>()));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Type Reflected type to which the meta function is associated.
|
||||
* @tparam Candidate The actual function to associate with the reflected type.
|
||||
*/
|
||||
template<typename Type, typename Candidate>
|
||||
using meta_function_helper_t = typename meta_function_helper<Type, Candidate>::type;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns the meta type of the i-th element of a list of arguments.
|
||||
* @tparam Args Actual types of arguments.
|
||||
* @return The meta type of the i-th element of the list of arguments.
|
||||
*/
|
||||
template<typename... Args>
|
||||
[[nodiscard]] static meta_type meta_arg(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT {
|
||||
return internal::meta_arg_node(type_list<Args...>{}, index);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Constructs an instance given a list of erased parameters, if possible.
|
||||
* @tparam Type Actual type of the instance to construct.
|
||||
* @tparam Args Types of arguments expected.
|
||||
* @tparam Index Indexes to use to extract erased arguments from their list.
|
||||
* @param args Parameters to use to construct the instance.
|
||||
* @return A meta any containing the new instance, if any.
|
||||
*/
|
||||
template<typename Type, typename... Args, std::size_t... Index>
|
||||
[[nodiscard]] meta_any meta_construct(meta_any * const args, std::index_sequence<Index...>) {
|
||||
if(((args+Index)->allow_cast<Args>() && ...)) {
|
||||
return Type{(args+Index)->cast<Args>()...};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Sets the value of a given variable.
|
||||
* @tparam Type Reflected type to which the variable is associated.
|
||||
* @tparam Data The actual variable to set.
|
||||
* @param instance An opaque instance of the underlying type, if required.
|
||||
* @param value Parameter to use to set the variable.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
template<typename Type, auto Data>
|
||||
[[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) {
|
||||
if constexpr(!std::is_same_v<decltype(Data), Type> && !std::is_same_v<decltype(Data), std::nullptr_t>) {
|
||||
if constexpr(std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>> || std::is_member_function_pointer_v<decltype(Data)>) {
|
||||
using descriptor = meta_function_helper_t<Type, decltype(Data)>;
|
||||
using data_type = type_list_element_t<!std::is_member_function_pointer_v<decltype(Data)>, typename descriptor::args_type>;
|
||||
|
||||
if(auto * const clazz = instance->try_cast<Type>(); clazz) {
|
||||
if(value.allow_cast<data_type>()) {
|
||||
std::invoke(Data, *clazz, value.cast<data_type>());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
|
||||
using data_type = std::remove_reference_t<decltype(std::declval<Type>().*Data)>;
|
||||
|
||||
if constexpr(!std::is_array_v<data_type> && !std::is_const_v<data_type>) {
|
||||
if(auto * const clazz = instance->try_cast<Type>(); clazz) {
|
||||
if(value.allow_cast<data_type>()) {
|
||||
std::invoke(Data, clazz) = value.cast<data_type>();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
using data_type = std::remove_reference_t<decltype(*Data)>;
|
||||
|
||||
if constexpr(!std::is_array_v<data_type> && !std::is_const_v<data_type>) {
|
||||
if(value.allow_cast<data_type>()) {
|
||||
*Data = value.cast<data_type>();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Gets the value of a given variable.
|
||||
* @tparam Type Reflected type to which the variable is associated.
|
||||
* @tparam Data The actual variable to get.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param instance An opaque instance of the underlying type, if required.
|
||||
* @return A meta any containing the value of the underlying variable.
|
||||
*/
|
||||
template<typename Type, auto Data, typename Policy = as_is_t>
|
||||
[[nodiscard]] meta_any meta_getter([[maybe_unused]] meta_handle instance) {
|
||||
[[maybe_unused]] auto dispatch = [](auto &&value) {
|
||||
if constexpr(std::is_same_v<Policy, as_void_t>) {
|
||||
return meta_any{std::in_place_type<void>, std::forward<decltype(value)>(value)};
|
||||
} else if constexpr(std::is_same_v<Policy, as_ref_t>) {
|
||||
return meta_any{std::reference_wrapper{std::forward<decltype(value)>(value)}};
|
||||
} else if constexpr(std::is_same_v<Policy, as_cref_t>) {
|
||||
return meta_any{std::cref(std::forward<decltype(value)>(value))};
|
||||
} else {
|
||||
static_assert(std::is_same_v<Policy, as_is_t>, "Policy not supported");
|
||||
return meta_any{std::forward<decltype(value)>(value)};
|
||||
}
|
||||
};
|
||||
|
||||
if constexpr(std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>> || std::is_member_function_pointer_v<decltype(Data)>) {
|
||||
auto * const clazz = instance->try_cast<std::conditional_t<std::is_invocable_v<decltype(Data), const Type &>, const Type, Type>>();
|
||||
return clazz ? dispatch(std::invoke(Data, *clazz)) : meta_any{};
|
||||
} else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
|
||||
if constexpr(std::is_array_v<std::remove_cv_t<std::remove_reference_t<decltype(std::declval<Type>().*Data)>>>) {
|
||||
return meta_any{};
|
||||
} else {
|
||||
if(auto * clazz = instance->try_cast<Type>(); clazz) {
|
||||
return dispatch(std::invoke(Data, *clazz));
|
||||
} else {
|
||||
auto * fallback = instance->try_cast<const Type>();
|
||||
return fallback ? dispatch(std::invoke(Data, *fallback)) : meta_any{};
|
||||
}
|
||||
}
|
||||
} else if constexpr(std::is_pointer_v<decltype(Data)>) {
|
||||
if constexpr(std::is_array_v<std::remove_pointer_t<decltype(Data)>>) {
|
||||
return meta_any{};
|
||||
} else {
|
||||
return dispatch(*Data);
|
||||
}
|
||||
} else {
|
||||
return dispatch(Data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Invokes a function given a list of erased parameters, if possible.
|
||||
* @tparam Type Reflected type to which the function is associated.
|
||||
* @tparam Candidate The actual function to invoke.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @tparam Index Indexes to use to extract erased arguments from their list.
|
||||
* @param instance An opaque instance of the underlying type, if required.
|
||||
* @param args Parameters to use to invoke the function.
|
||||
* @return A meta any containing the returned value, if any.
|
||||
*/
|
||||
template<typename Type, auto Candidate, typename Policy = as_is_t, std::size_t... Index>
|
||||
[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, meta_any *args, std::index_sequence<Index...>) {
|
||||
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
|
||||
|
||||
auto dispatch = [](auto &&... params) {
|
||||
if constexpr(std::is_void_v<std::remove_cv_t<typename descriptor::return_type>> || std::is_same_v<Policy, as_void_t>) {
|
||||
std::invoke(Candidate, std::forward<decltype(params)>(params)...);
|
||||
return meta_any{std::in_place_type<void>};
|
||||
} else if constexpr(std::is_same_v<Policy, as_ref_t>) {
|
||||
return meta_any{std::reference_wrapper{std::invoke(Candidate, std::forward<decltype(params)>(params)...)}};
|
||||
} else if constexpr(std::is_same_v<Policy, as_cref_t>) {
|
||||
return meta_any{std::cref(std::invoke(Candidate, std::forward<decltype(params)>(params)...))};
|
||||
} else {
|
||||
static_assert(std::is_same_v<Policy, as_is_t>, "Policy not supported");
|
||||
return meta_any{std::invoke(Candidate, std::forward<decltype(params)>(params)...)};
|
||||
}
|
||||
};
|
||||
|
||||
if constexpr(std::is_invocable_v<decltype(Candidate), const Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
|
||||
if(const auto * const clazz = instance->try_cast<const Type>(); clazz && ((args+Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
|
||||
return dispatch(*clazz, (args+Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
|
||||
}
|
||||
} else if constexpr(std::is_invocable_v<decltype(Candidate), Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
|
||||
if(auto * const clazz = instance->try_cast<Type>(); clazz && ((args+Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
|
||||
return dispatch(*clazz, (args+Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
|
||||
}
|
||||
} else {
|
||||
if(((args+Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
|
||||
return dispatch((args+Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
|
||||
}
|
||||
}
|
||||
|
||||
return meta_any{};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#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
|
||||
26
src/entt/poly/fwd.hpp
Normal file
26
src/entt/poly/fwd.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef ENTT_POLY_FWD_HPP
|
||||
#define ENTT_POLY_FWD_HPP
|
||||
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
template<typename, std::size_t Len, std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
|
||||
class basic_poly;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Concept Concept descriptor.
|
||||
*/
|
||||
template<typename Concept>
|
||||
using poly = basic_poly<Concept, sizeof(double[2])>;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
351
src/entt/poly/poly.hpp
Normal file
351
src/entt/poly/poly.hpp
Normal file
@@ -0,0 +1,351 @@
|
||||
#ifndef ENTT_POLY_POLY_HPP
|
||||
#define ENTT_POLY_POLY_HPP
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/any.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/*! @brief Inspector class used to infer the type of the virtual table. */
|
||||
struct poly_inspector {
|
||||
/**
|
||||
* @brief Generic conversion operator (definition only).
|
||||
* @tparam Type Type to which conversion is requested.
|
||||
*/
|
||||
template <class Type>
|
||||
operator Type &&() const;
|
||||
|
||||
/**
|
||||
* @brief Dummy invocation function (definition only).
|
||||
* @tparam Member Index of the function to invoke.
|
||||
* @tparam Args Types of arguments to pass to the function.
|
||||
* @param args The arguments to pass to the function.
|
||||
* @return A poly inspector convertible to any type.
|
||||
*/
|
||||
template<auto Member, typename... Args>
|
||||
poly_inspector invoke(Args &&... args) const;
|
||||
|
||||
/*! @copydoc invoke */
|
||||
template<auto Member, typename... Args>
|
||||
poly_inspector invoke(Args &&... args);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Static virtual table factory.
|
||||
* @tparam Concept Concept descriptor.
|
||||
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||
* @tparam Align Alignment requirement.
|
||||
*/
|
||||
template<typename Concept, std::size_t Len, std::size_t Align>
|
||||
class poly_vtable {
|
||||
using inspector = typename Concept::template type<poly_inspector>;
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
static auto vtable_entry(Ret(*)(inspector &, Args...)) -> Ret(*)(basic_any<Len, Align> &, Args...);
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
static auto vtable_entry(Ret(*)(const inspector &, Args...)) -> Ret(*)(const basic_any<Len, Align> &, Args...);
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
static auto vtable_entry(Ret(*)(Args...)) -> Ret(*)(const basic_any<Len, Align> &, Args...);
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
static auto vtable_entry(Ret(inspector:: *)(Args...)) -> Ret(*)(basic_any<Len, Align> &, Args...);
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
static auto vtable_entry(Ret(inspector:: *)(Args...) const) -> Ret(*)(const basic_any<Len, Align> &, Args...);
|
||||
|
||||
template<auto... Candidate>
|
||||
static auto make_vtable(value_list<Candidate...>)
|
||||
-> decltype(std::make_tuple(vtable_entry(Candidate)...));
|
||||
|
||||
template<typename... Func>
|
||||
[[nodiscard]] static constexpr auto make_vtable(type_list<Func...>) {
|
||||
if constexpr(sizeof...(Func) == 0) {
|
||||
return decltype(make_vtable(typename Concept::template impl<inspector>{})){};
|
||||
} else if constexpr((std::is_function_v<Func> && ...)) {
|
||||
return decltype(std::make_tuple(vtable_entry(std::declval<Func inspector:: *>())...)){};
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Type, auto Candidate, typename Ret, typename Any, typename... Args>
|
||||
static void fill_vtable_entry(Ret(* &entry)(Any &, Args...)) {
|
||||
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
|
||||
entry = +[](Any &, Args... args) -> Ret {
|
||||
return std::invoke(Candidate, std::forward<Args>(args)...);
|
||||
};
|
||||
} else {
|
||||
entry = +[](Any &instance, Args... args) -> Ret {
|
||||
return static_cast<Ret>(std::invoke(Candidate, any_cast<constness_as_t<Type, Any> &>(instance), std::forward<Args>(args)...));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Type, auto... Index>
|
||||
[[nodiscard]] static auto fill_vtable(std::index_sequence<Index...>) {
|
||||
type impl{};
|
||||
(fill_vtable_entry<Type, value_list_element_v<Index, typename Concept::template impl<Type>>>(std::get<Index>(impl)), ...);
|
||||
return impl;
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Virtual table type. */
|
||||
using type = decltype(make_vtable(Concept{}));
|
||||
|
||||
/**
|
||||
* @brief Returns a static virtual table for a specific concept and type.
|
||||
* @tparam Type The type for which to generate the virtual table.
|
||||
* @return A static virtual table for the given concept and type.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] static const auto * instance() {
|
||||
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Type differs from its decayed form");
|
||||
static const auto vtable = fill_vtable<Type>(std::make_index_sequence<Concept::template impl<Type>::size>{});
|
||||
return &vtable;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Poly base class used to inject functionalities into concepts.
|
||||
* @tparam Poly The outermost poly class.
|
||||
*/
|
||||
template<typename Poly>
|
||||
struct poly_base {
|
||||
/**
|
||||
* @brief Invokes a function from the static virtual table.
|
||||
* @tparam Member Index of the function to invoke.
|
||||
* @tparam Args Types of arguments to pass to the function.
|
||||
* @param self A reference to the poly object that made the call.
|
||||
* @param args The arguments to pass to the function.
|
||||
* @return The return value of the invoked function, if any.
|
||||
*/
|
||||
template<auto Member, typename... Args>
|
||||
[[nodiscard]] decltype(auto) invoke(const poly_base &self, Args &&... args) const {
|
||||
const auto &poly = static_cast<const Poly &>(self);
|
||||
return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/*! @copydoc invoke */
|
||||
template<auto Member, typename... Args>
|
||||
[[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&... args) {
|
||||
auto &poly = static_cast<Poly &>(self);
|
||||
return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Shortcut for calling `poly_base<Type>::invoke`.
|
||||
* @tparam Member Index of the function to invoke.
|
||||
* @tparam Poly A fully defined poly object.
|
||||
* @tparam Args Types of arguments to pass to the function.
|
||||
* @param self A reference to the poly object that made the call.
|
||||
* @param args The arguments to pass to the function.
|
||||
* @return The return value of the invoked function, if any.
|
||||
*/
|
||||
template<auto Member, typename Poly, typename... Args>
|
||||
decltype(auto) poly_call(Poly &&self, Args &&... args) {
|
||||
return std::forward<Poly>(self).template invoke<Member>(self, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Static polymorphism made simple and within everyone's reach.
|
||||
*
|
||||
* Static polymorphism is a very powerful tool in C++, albeit sometimes
|
||||
* cumbersome to obtain.<br/>
|
||||
* This class aims to make it simple and easy to use.
|
||||
*
|
||||
* @note
|
||||
* Both deduced and defined static virtual tables are supported.<br/>
|
||||
* Moreover, the `poly` class template also works with unmanaged objects.
|
||||
*
|
||||
* @tparam Concept Concept descriptor.
|
||||
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||
* @tparam Align Optional alignment requirement.
|
||||
*/
|
||||
template<typename Concept, std::size_t Len, std::size_t Align>
|
||||
class basic_poly: private Concept::template type<poly_base<basic_poly<Concept, Len, Align>>> {
|
||||
/*! @brief A poly base is allowed to snoop into a poly object. */
|
||||
friend struct poly_base<basic_poly>;
|
||||
|
||||
using vtable_type = typename poly_vtable<Concept, Len, Align>::type;
|
||||
|
||||
public:
|
||||
/*! @brief Concept type. */
|
||||
using concept_type = typename Concept::template type<poly_base<basic_poly>>;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_poly() ENTT_NOEXCEPT
|
||||
: storage{},
|
||||
vtable{}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Constructs a poly by directly initializing the new object.
|
||||
* @tparam Type Type of object to use to initialize the poly.
|
||||
* @tparam Args Types of arguments to use to construct the new instance.
|
||||
* @param args Parameters to use to construct the instance.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
explicit basic_poly(std::in_place_type_t<Type>, Args &&... args)
|
||||
: storage{std::in_place_type<Type>, std::forward<Args>(args)...},
|
||||
vtable{poly_vtable<Concept, Len, Align>::template instance<std::remove_const_t<std::remove_reference_t<Type>>>()}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Constructs a poly that holds an unmanaged object.
|
||||
* @tparam Type Type of object to use to initialize the poly.
|
||||
* @param value An instance of an object to use to initialize the poly.
|
||||
*/
|
||||
template<typename Type>
|
||||
basic_poly(std::reference_wrapper<Type> value)
|
||||
: basic_poly{std::in_place_type<Type &>, value.get()}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Constructs a poly from a given value.
|
||||
* @tparam Type Type of object to use to initialize the poly.
|
||||
* @param value An instance of an object to use to initialize the poly.
|
||||
*/
|
||||
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, basic_poly>>>
|
||||
basic_poly(Type &&value) ENTT_NOEXCEPT
|
||||
: basic_poly{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
*/
|
||||
basic_poly(const basic_poly &other) = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_poly(basic_poly &&other) ENTT_NOEXCEPT
|
||||
: basic_poly{}
|
||||
{
|
||||
swap(*this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assignment operator.
|
||||
* @param other The instance to assign from.
|
||||
* @return This poly object.
|
||||
*/
|
||||
basic_poly & operator=(basic_poly other) {
|
||||
swap(other, *this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the type of the contained object.
|
||||
* @return The type of the contained object, if any.
|
||||
*/
|
||||
[[nodiscard]] type_info type() const ENTT_NOEXCEPT {
|
||||
return storage.type();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an opaque pointer to the contained instance.
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
[[nodiscard]] const void * data() const ENTT_NOEXCEPT {
|
||||
return storage.data();
|
||||
}
|
||||
|
||||
/*! @copydoc data */
|
||||
[[nodiscard]] void * data() ENTT_NOEXCEPT {
|
||||
return storage.data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Replaces the contained object by creating a new instance directly.
|
||||
* @tparam Type Type of object to use to initialize the poly.
|
||||
* @tparam Args Types of arguments to use to construct the new instance.
|
||||
* @param args Parameters to use to construct the instance.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
void emplace(Args &&... args) {
|
||||
*this = basic_poly{std::in_place_type<Type>, std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
/*! @brief Destroys contained object */
|
||||
void reset() {
|
||||
*this = basic_poly{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns false if a poly is empty, true otherwise.
|
||||
* @return False if the poly is empty, true otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return !(vtable == nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to the underlying concept.
|
||||
* @return A pointer to the underlying concept.
|
||||
*/
|
||||
[[nodiscard]] concept_type * operator->() ENTT_NOEXCEPT {
|
||||
return this;
|
||||
}
|
||||
|
||||
/*! @copydoc operator-> */
|
||||
[[nodiscard]] const concept_type * operator->() const ENTT_NOEXCEPT {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swaps two poly objects.
|
||||
* @param lhs A valid poly object.
|
||||
* @param rhs A valid poly object.
|
||||
*/
|
||||
friend void swap(basic_poly &lhs, basic_poly &rhs) {
|
||||
using std::swap;
|
||||
swap(lhs.storage, rhs.storage);
|
||||
swap(lhs.vtable, rhs.vtable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Aliasing constructor.
|
||||
* @return A poly that shares a reference to an unmanaged object.
|
||||
*/
|
||||
[[nodiscard]] basic_poly as_ref() ENTT_NOEXCEPT {
|
||||
basic_poly ref = std::as_const(*this).as_ref();
|
||||
ref.storage = storage.as_ref();
|
||||
return ref;
|
||||
}
|
||||
|
||||
/*! @copydoc as_ref */
|
||||
[[nodiscard]] basic_poly as_ref() const ENTT_NOEXCEPT {
|
||||
basic_poly ref{};
|
||||
ref.storage = storage.as_ref();
|
||||
ref.vtable = vtable;
|
||||
return ref;
|
||||
}
|
||||
|
||||
private:
|
||||
basic_any<Len, Align> storage;
|
||||
const vtable_type *vtable;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -2,9 +2,9 @@
|
||||
#define ENTT_PROCESS_PROCESS_HPP
|
||||
|
||||
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
|
||||
|
||||
namespace entt {
|
||||
@@ -19,33 +19,43 @@ namespace entt {
|
||||
* required:
|
||||
*
|
||||
* * @code{.cpp}
|
||||
* void update(Delta);
|
||||
* void update(Delta, void *);
|
||||
* @endcode
|
||||
*
|
||||
* It's invoked once per tick until a process is explicitly aborted or it
|
||||
* terminates either with or without errors. Even though it's not mandatory to
|
||||
* declare this member function, as a rule of thumb each process should at
|
||||
* least define it to work properly.
|
||||
* least define it to work properly. The `void *` parameter is an opaque
|
||||
* pointer to user data (if any) forwarded directly to the process during an
|
||||
* update.
|
||||
*
|
||||
* * @code{.cpp}
|
||||
* void init();
|
||||
* @endcode
|
||||
* It's invoked at the first tick, immediately before an update.
|
||||
*
|
||||
* It's invoked when the process joins the running queue of a scheduler. This
|
||||
* happens as soon as it's attached to the scheduler if the process is a top
|
||||
* level one, otherwise when it replaces its parent if the process is a
|
||||
* continuation.
|
||||
*
|
||||
* * @code{.cpp}
|
||||
* void succeeded();
|
||||
* @endcode
|
||||
*
|
||||
* It's invoked in case of success, immediately after an update and during the
|
||||
* same tick.
|
||||
*
|
||||
* * @code{.cpp}
|
||||
* void failed();
|
||||
* @endcode
|
||||
*
|
||||
* It's invoked in case of errors, immediately after an update and during the
|
||||
* same tick.
|
||||
*
|
||||
* * @code{.cpp}
|
||||
* void aborted();
|
||||
* @endcode
|
||||
*
|
||||
* It's invoked only if a process is explicitly aborted. There is no guarantee
|
||||
* that it executes in the same tick, this depends solely on whether the
|
||||
* process is aborted immediately or not.
|
||||
@@ -54,58 +64,55 @@ namespace entt {
|
||||
* `succeed` and `fail` protected member functions and even pause or unpause the
|
||||
* process itself.
|
||||
*
|
||||
* @sa Scheduler
|
||||
* @sa scheduler
|
||||
*
|
||||
* @tparam Derived Actual type of process that extends the class template.
|
||||
* @tparam Delta Type to use to provide elapsed time.
|
||||
*/
|
||||
template<typename Derived, typename Delta>
|
||||
class Process {
|
||||
enum class State: unsigned int {
|
||||
class process {
|
||||
enum class state: unsigned int {
|
||||
UNINITIALIZED = 0,
|
||||
RUNNING,
|
||||
PAUSED,
|
||||
SUCCEEDED,
|
||||
FAILED,
|
||||
ABORTED,
|
||||
FINISHED
|
||||
FINISHED,
|
||||
REJECTED
|
||||
};
|
||||
|
||||
template<State state>
|
||||
using tag = std::integral_constant<State, state>;
|
||||
|
||||
template<typename Target = Derived>
|
||||
auto tick(int, tag<State::UNINITIALIZED>, void *data)
|
||||
-> decltype(std::declval<Target>().init(data)) {
|
||||
static_cast<Target *>(this)->init(data);
|
||||
auto next(std::integral_constant<state, state::UNINITIALIZED>)
|
||||
-> decltype(std::declval<Target>().init(), void()) {
|
||||
static_cast<Target *>(this)->init();
|
||||
}
|
||||
|
||||
template<typename Target = Derived>
|
||||
auto tick(int, tag<State::RUNNING>, Delta delta, void *data)
|
||||
-> decltype(std::declval<Target>().update(delta, data)) {
|
||||
auto next(std::integral_constant<state, 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, tag<State::SUCCEEDED>)
|
||||
-> decltype(std::declval<Target>().succeeded()) {
|
||||
auto next(std::integral_constant<state, state::SUCCEEDED>)
|
||||
-> decltype(std::declval<Target>().succeeded(), void()) {
|
||||
static_cast<Target *>(this)->succeeded();
|
||||
}
|
||||
|
||||
template<typename Target = Derived>
|
||||
auto tick(int, tag<State::FAILED>)
|
||||
-> decltype(std::declval<Target>().failed()) {
|
||||
auto next(std::integral_constant<state, state::FAILED>)
|
||||
-> decltype(std::declval<Target>().failed(), void()) {
|
||||
static_cast<Target *>(this)->failed();
|
||||
}
|
||||
|
||||
template<typename Target = Derived>
|
||||
auto tick(int, tag<State::ABORTED>)
|
||||
-> decltype(std::declval<Target>().aborted()) {
|
||||
auto next(std::integral_constant<state, state::ABORTED>)
|
||||
-> decltype(std::declval<Target>().aborted(), void()) {
|
||||
static_cast<Target *>(this)->aborted();
|
||||
}
|
||||
|
||||
template<State S, typename... Args>
|
||||
void tick(char, tag<S>, Args&&...) {}
|
||||
void next(...) const ENTT_NOEXCEPT {}
|
||||
|
||||
protected:
|
||||
/**
|
||||
@@ -114,9 +121,9 @@ protected:
|
||||
* The function is idempotent and it does nothing if the process isn't
|
||||
* alive.
|
||||
*/
|
||||
void succeed() noexcept {
|
||||
void succeed() ENTT_NOEXCEPT {
|
||||
if(alive()) {
|
||||
current = State::SUCCEEDED;
|
||||
current = state::SUCCEEDED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,9 +133,9 @@ protected:
|
||||
* The function is idempotent and it does nothing if the process isn't
|
||||
* alive.
|
||||
*/
|
||||
void fail() noexcept {
|
||||
void fail() ENTT_NOEXCEPT {
|
||||
if(alive()) {
|
||||
current = State::FAILED;
|
||||
current = state::FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,9 +145,9 @@ protected:
|
||||
* The function is idempotent and it does nothing if the process isn't
|
||||
* running.
|
||||
*/
|
||||
void pause() noexcept {
|
||||
if(current == State::RUNNING) {
|
||||
current = State::PAUSED;
|
||||
void pause() ENTT_NOEXCEPT {
|
||||
if(current == state::RUNNING) {
|
||||
current = state::PAUSED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,9 +157,9 @@ protected:
|
||||
* The function is idempotent and it does nothing if the process isn't
|
||||
* paused.
|
||||
*/
|
||||
void unpause() noexcept {
|
||||
if(current == State::PAUSED) {
|
||||
current = State::RUNNING;
|
||||
void unpause() ENTT_NOEXCEPT {
|
||||
if(current == state::PAUSED) {
|
||||
current = state::RUNNING;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,8 +168,8 @@ public:
|
||||
using delta_type = Delta;
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~Process() noexcept {
|
||||
static_assert(std::is_base_of<Process, Derived>::value, "!");
|
||||
virtual ~process() {
|
||||
static_assert(std::is_base_of_v<process, Derived>, "Incorrect use of the class template");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,12 +180,12 @@ public:
|
||||
*
|
||||
* @param immediately Requests an immediate operation.
|
||||
*/
|
||||
void abort(bool immediately = false) noexcept {
|
||||
void abort(const bool immediately = false) {
|
||||
if(alive()) {
|
||||
current = State::ABORTED;
|
||||
current = state::ABORTED;
|
||||
|
||||
if(immediately) {
|
||||
tick(0);
|
||||
tick({});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,32 +194,32 @@ 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 noexcept {
|
||||
return current == State::RUNNING || current == State::PAUSED;
|
||||
[[nodiscard]] bool alive() const ENTT_NOEXCEPT {
|
||||
return current == state::RUNNING || current == state::PAUSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if a process is already terminated.
|
||||
* @return True if the process is terminated, false otherwise.
|
||||
*/
|
||||
bool dead() const noexcept {
|
||||
return current == State::FINISHED;
|
||||
[[nodiscard]] bool dead() const ENTT_NOEXCEPT {
|
||||
return current == state::FINISHED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if a process is currently paused.
|
||||
* @return True if the process is paused, false otherwise.
|
||||
*/
|
||||
bool paused() const noexcept {
|
||||
return current == State::PAUSED;
|
||||
[[nodiscard]] bool paused() const ENTT_NOEXCEPT {
|
||||
return current == state::PAUSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if a process terminated with errors.
|
||||
* @return True if the process terminated with errors, false otherwise.
|
||||
*/
|
||||
bool rejected() const noexcept {
|
||||
return stopped;
|
||||
[[nodiscard]] bool rejected() const ENTT_NOEXCEPT {
|
||||
return current == state::REJECTED;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,14 +227,15 @@ public:
|
||||
* @param delta Elapsed time.
|
||||
* @param data Optional data.
|
||||
*/
|
||||
void tick(Delta delta, void *data = nullptr) {
|
||||
void tick(const Delta delta, void *data = nullptr) {
|
||||
switch (current) {
|
||||
case State::UNINITIALIZED:
|
||||
tick(0, tag<State::UNINITIALIZED>{}, data);
|
||||
current = State::RUNNING;
|
||||
// no break on purpose, tasks are executed immediately
|
||||
case State::RUNNING:
|
||||
tick(0, tag<State::RUNNING>{}, delta, data);
|
||||
case state::UNINITIALIZED:
|
||||
next(std::integral_constant<state, state::UNINITIALIZED>{});
|
||||
current = state::RUNNING;
|
||||
break;
|
||||
case state::RUNNING:
|
||||
next(std::integral_constant<state, state::RUNNING>{}, delta, data);
|
||||
break;
|
||||
default:
|
||||
// suppress warnings
|
||||
break;
|
||||
@@ -235,19 +243,17 @@ public:
|
||||
|
||||
// if it's dead, it must be notified and removed immediately
|
||||
switch(current) {
|
||||
case State::SUCCEEDED:
|
||||
tick(0, tag<State::SUCCEEDED>{});
|
||||
current = State::FINISHED;
|
||||
case state::SUCCEEDED:
|
||||
next(std::integral_constant<state, state::SUCCEEDED>{});
|
||||
current = state::FINISHED;
|
||||
break;
|
||||
case State::FAILED:
|
||||
tick(0, tag<State::FAILED>{});
|
||||
current = State::FINISHED;
|
||||
stopped = true;
|
||||
case state::FAILED:
|
||||
next(std::integral_constant<state, state::FAILED>{});
|
||||
current = state::REJECTED;
|
||||
break;
|
||||
case State::ABORTED:
|
||||
tick(0, tag<State::ABORTED>{});
|
||||
current = State::FINISHED;
|
||||
stopped = true;
|
||||
case state::ABORTED:
|
||||
next(std::integral_constant<state, state::ABORTED>{});
|
||||
current = state::REJECTED;
|
||||
break;
|
||||
default:
|
||||
// suppress warnings
|
||||
@@ -256,8 +262,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
State current{State::UNINITIALIZED};
|
||||
bool stopped{false};
|
||||
state current{state::UNINITIALIZED};
|
||||
};
|
||||
|
||||
|
||||
@@ -273,12 +278,13 @@ private:
|
||||
* following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(Delta delta, auto succeed, auto fail);
|
||||
* void(Delta delta, void *data, auto succeed, auto fail);
|
||||
* @endcode
|
||||
*
|
||||
* Where:
|
||||
*
|
||||
* * `delta` is the elapsed time.
|
||||
* * `data` is an opaque pointer to user data if any, `nullptr` otherwise.
|
||||
* * `succeed` is a function to call when a process terminates with success.
|
||||
* * `fail` is a function to call when a process terminates with errors.
|
||||
*
|
||||
@@ -293,21 +299,21 @@ private:
|
||||
* create them internally each and avery time a lambda or a functor is used as
|
||||
* a process.
|
||||
*
|
||||
* @sa Process
|
||||
* @sa Scheduler
|
||||
* @sa process
|
||||
* @sa scheduler
|
||||
*
|
||||
* @tparam Func Actual type of process.
|
||||
* @tparam Delta Type to use to provide elapsed time.
|
||||
*/
|
||||
template<typename Func, typename Delta>
|
||||
struct ProcessAdaptor: Process<ProcessAdaptor<Func, Delta>, Delta>, private Func {
|
||||
struct process_adaptor: process<process_adaptor<Func, Delta>, Delta>, private Func {
|
||||
/**
|
||||
* @brief Constructs a process adaptor from a lambda or a functor.
|
||||
* @tparam Args Types of arguments to use to initialize the actual process.
|
||||
* @param args Parameters to use to initialize the actual process.
|
||||
*/
|
||||
template<typename... Args>
|
||||
ProcessAdaptor(Args&&... args)
|
||||
process_adaptor(Args &&... args)
|
||||
: Func{std::forward<Args>(args)...}
|
||||
{}
|
||||
|
||||
@@ -316,8 +322,8 @@ struct ProcessAdaptor: Process<ProcessAdaptor<Func, Delta>, Delta>, private Func
|
||||
* @param delta Elapsed time.
|
||||
* @param data Optional data.
|
||||
*/
|
||||
void update(Delta delta, void *data) {
|
||||
Func::operator()(delta, data, [this](){ this->succeed(); }, [this](){ this->fail(); });
|
||||
void update(const Delta delta, void *data) {
|
||||
Func::operator()(delta, data, [this]() { this->succeed(); }, [this]() { this->fail(); });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -325,4 +331,4 @@ struct ProcessAdaptor: Process<ProcessAdaptor<Func, Delta>, Delta>, private Func
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_PROCESS_PROCESS_HPP
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user