Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4e58bdd36 | ||
|
|
8375ee0910 | ||
|
|
137339f48c | ||
|
|
713bcd50a3 | ||
|
|
03a45d9cca | ||
|
|
cffbf89d9b | ||
|
|
f48d16a6e6 | ||
|
|
4905da2637 | ||
|
|
558109bbe1 | ||
|
|
a505d41266 | ||
|
|
9381900b00 | ||
|
|
f6a3b09ec2 | ||
|
|
51c7533265 | ||
|
|
d502bf18da | ||
|
|
db263d2ff9 | ||
|
|
16fae2d625 | ||
|
|
4e3a1a8428 | ||
|
|
aec8a6588c | ||
|
|
3160d991f7 | ||
|
|
7359018880 | ||
|
|
17fb301636 | ||
|
|
7dac5af05d | ||
|
|
d15e514f10 | ||
|
|
fe8320c66f | ||
|
|
7111549c7c | ||
|
|
e8e5f5ee2b | ||
|
|
9caaf569e6 | ||
|
|
03bf0f90b4 | ||
|
|
5df7142f51 | ||
|
|
4d95793123 | ||
|
|
0aa503e69e | ||
|
|
02323e814a | ||
|
|
440fed990d | ||
|
|
c2dcd7257f | ||
|
|
716e3257b1 | ||
|
|
f781441bcd | ||
|
|
3c0aa4d300 | ||
|
|
a57e1a971d | ||
|
|
dc8da9e42c | ||
|
|
7eeb828980 | ||
|
|
b0d7490829 | ||
|
|
0d678442a0 | ||
|
|
2dff7c9d55 | ||
|
|
3fc5de2916 | ||
|
|
a572671947 | ||
|
|
2a6f769092 | ||
|
|
6a25c0df84 | ||
|
|
72127d699a | ||
|
|
b52d0aa133 | ||
|
|
5d006fefcc | ||
|
|
d7c58d00b4 | ||
|
|
9198a27c25 | ||
|
|
31d5d5ff1c | ||
|
|
f3fa359edf | ||
|
|
cba31e00ea | ||
|
|
d366a5ed92 | ||
|
|
d92bea4afd | ||
|
|
bbfd458d2d | ||
|
|
87a114ccc6 | ||
|
|
6852415ffc | ||
|
|
15f65bd58e | ||
|
|
54c7c47e3d | ||
|
|
5e6ccdedc8 | ||
|
|
dadf493aca | ||
|
|
3e5842a467 | ||
|
|
75ae0a10d0 | ||
|
|
e87a8e4b63 | ||
|
|
41282b0ecc | ||
|
|
030095f48d | ||
|
|
e18061bf86 | ||
|
|
38bcbf41a3 | ||
|
|
602626c840 | ||
|
|
41e359502d | ||
|
|
8e64f8accf | ||
|
|
0afab946da | ||
|
|
20d77024f7 | ||
|
|
b1bb9b4b5b | ||
|
|
978e003dbc | ||
|
|
47c65a20a1 | ||
|
|
31d845a5f5 | ||
|
|
03c5661df9 | ||
|
|
48644e568f | ||
|
|
19c0e8029d | ||
|
|
3562bec4a7 | ||
|
|
e863e4fe54 | ||
|
|
c5d89597fc | ||
|
|
091eef0e4c | ||
|
|
96b56ca630 | ||
|
|
56d78c767a | ||
|
|
1f57b2795f | ||
|
|
de054655ab | ||
|
|
bee74568e5 | ||
|
|
ebb2af1225 | ||
|
|
1487ad292b | ||
|
|
d3b8e92294 | ||
|
|
ab1d7d0126 | ||
|
|
01b21afca0 | ||
|
|
ef0056908a | ||
|
|
b021c78550 | ||
|
|
aedaa7f6a4 | ||
|
|
e15af7b500 | ||
|
|
38b34eb986 | ||
|
|
b4fc627c99 | ||
|
|
c9364d3af5 | ||
|
|
baa0d2c23c | ||
|
|
dc154a4b8a | ||
|
|
ba0e9fab45 | ||
|
|
4ab5a375e8 | ||
|
|
f63f36a411 | ||
|
|
f2b8177aaf | ||
|
|
53fab33423 | ||
|
|
4a252bdb84 | ||
|
|
cece4be1ec | ||
|
|
8f23d743ca | ||
|
|
0fa1bc5999 | ||
|
|
281276e84e | ||
|
|
f73409e9ad | ||
|
|
740cf006d9 | ||
|
|
828ecb57e6 | ||
|
|
8794888c98 | ||
|
|
dd7b96fc9e | ||
|
|
1116c33ada | ||
|
|
38fe05db85 | ||
|
|
b2d4832842 | ||
|
|
4c19d2844e | ||
|
|
6f2e5090e1 | ||
|
|
25e5123128 | ||
|
|
8c3ca20d19 | ||
|
|
ee7f24be63 | ||
|
|
a5f58ef49f | ||
|
|
ba5c4ba121 | ||
|
|
fcf455f7ef | ||
|
|
6836d5b024 | ||
|
|
8142136940 | ||
|
|
e76bbb2015 | ||
|
|
6f57622eff | ||
|
|
0c5cab164a | ||
|
|
c67a62de81 | ||
|
|
80eba8afb4 | ||
|
|
0d6615a767 | ||
|
|
fc4d18ce44 | ||
|
|
ddf28480dc | ||
|
|
f49f2331ba | ||
|
|
6526d67d16 | ||
|
|
4d90521fcf | ||
|
|
9165a1df1e | ||
|
|
a1406baf61 | ||
|
|
645b3563a9 | ||
|
|
91eecc1efd | ||
|
|
0782855880 | ||
|
|
b9bfeb9b3d | ||
|
|
0409aab6bb | ||
|
|
947d89844b | ||
|
|
ff8544f97d | ||
|
|
c777cda857 | ||
|
|
a90584f1ce | ||
|
|
4e47c535c4 | ||
|
|
84fee2848f | ||
|
|
42259fb1ce | ||
|
|
e38e88362f | ||
|
|
14be4141fa | ||
|
|
6a4ca4c4ba | ||
|
|
71e61b5270 | ||
|
|
affe6ca7b8 | ||
|
|
f0a07c4530 | ||
|
|
a003575811 | ||
|
|
be3f022445 | ||
|
|
4b5b3fec41 | ||
|
|
4debb79a60 | ||
|
|
f3b763d973 | ||
|
|
356024ed6e | ||
|
|
e8e62638fc | ||
|
|
6ecf1f35c0 | ||
|
|
05863c6a15 | ||
|
|
1a02b6ad57 | ||
|
|
5ee1f3fec1 | ||
|
|
f44c9a1c05 | ||
|
|
715e5318de | ||
|
|
211e5fddc1 | ||
|
|
9347e91db6 | ||
|
|
bebfbada68 | ||
|
|
c239821d12 | ||
|
|
293cbe2769 | ||
|
|
3587eaed9a | ||
|
|
cb9d8ac3f0 | ||
|
|
e544755bbc | ||
|
|
e2003ff17a | ||
|
|
6232c93907 | ||
|
|
6153cdcbbe | ||
|
|
89af917748 | ||
|
|
28466a7316 | ||
|
|
33efea3f03 | ||
|
|
55f1a47fcc | ||
|
|
e9b3eee6d3 | ||
|
|
b400927f02 | ||
|
|
a6d566ae4a | ||
|
|
93979e13ac | ||
|
|
a77e40093d | ||
|
|
695ac44673 | ||
|
|
1cc078e4fb | ||
|
|
94cd89a72a | ||
|
|
5497291c29 | ||
|
|
3c75f7e2fd | ||
|
|
03d8cccc86 | ||
|
|
47bfffc7cc | ||
|
|
9795f60bc1 | ||
|
|
b3b48db957 | ||
|
|
7d2b4fcf57 | ||
|
|
3af8525eb8 | ||
|
|
3306b72f87 | ||
|
|
91a239a277 | ||
|
|
fdaaeea52d | ||
|
|
d81e57daa1 | ||
|
|
ed84c6e9ff | ||
|
|
fe872f6a0d | ||
|
|
d8aa90234a | ||
|
|
28d8041da6 | ||
|
|
1f53d163b7 | ||
|
|
7f38b7668e | ||
|
|
dc70d97760 | ||
|
|
4d0d48f7d9 | ||
|
|
4ecaf18b20 | ||
|
|
30a66cf513 | ||
|
|
9bbc8ad014 | ||
|
|
626b882f26 | ||
|
|
9b44fa47d0 | ||
|
|
9170fc850b | ||
|
|
973f010d85 | ||
|
|
6241543fe2 | ||
|
|
5e99452cf3 | ||
|
|
d058ccd084 | ||
|
|
7e4694ce69 | ||
|
|
b3b7975290 | ||
|
|
5ee0a39616 | ||
|
|
27b9a25d06 | ||
|
|
9f5a6260ce | ||
|
|
b4b85c8d53 | ||
|
|
4bc11e4297 | ||
|
|
ad291d4280 | ||
|
|
f61fd4584a | ||
|
|
536dba3048 | ||
|
|
4760a09d61 | ||
|
|
47ff98e25b | ||
|
|
a638df6cae | ||
|
|
4f1626fce9 | ||
|
|
9b3965c2c9 | ||
|
|
536a43be90 | ||
|
|
09b0e7f528 | ||
|
|
76b6841c51 | ||
|
|
4507c546c1 | ||
|
|
add999d105 | ||
|
|
9cefbc3c79 | ||
|
|
22347cfbd0 | ||
|
|
6966eabd25 | ||
|
|
a32e004c92 | ||
|
|
49d9385aa4 | ||
|
|
9970ba2284 | ||
|
|
20cc85011e | ||
|
|
7f5d4fb8d2 | ||
|
|
03e046f880 | ||
|
|
436f077e0b | ||
|
|
d76cfb06f0 | ||
|
|
2bb947f6de | ||
|
|
8a8f8dfd1b | ||
|
|
15b8db6f61 | ||
|
|
05e0a22fb3 | ||
|
|
ada11c4be1 | ||
|
|
7dbb59adc9 | ||
|
|
02440e17ae | ||
|
|
fa234af36b | ||
|
|
40fdd26788 | ||
|
|
6fa668900e | ||
|
|
17e88e506c | ||
|
|
3bcbea785a | ||
|
|
3bac3d4bbe | ||
|
|
abdb293106 | ||
|
|
f4b960dc93 | ||
|
|
71c217da28 | ||
|
|
4777ca2980 | ||
|
|
4aabca2f0b | ||
|
|
5c31fda9bd | ||
|
|
eecd1dc636 | ||
|
|
998f4b45da | ||
|
|
fb0b7e2aab | ||
|
|
cbb50c1292 | ||
|
|
fcd265a6ed | ||
|
|
357ee3f994 | ||
|
|
e35eed905b | ||
|
|
7942648035 | ||
|
|
93dca93616 | ||
|
|
9f10d32b73 | ||
|
|
21e82abfa7 | ||
|
|
7b48069bd5 | ||
|
|
5721e62ed0 | ||
|
|
e140399725 | ||
|
|
7f1a2b1887 | ||
|
|
0fd70878b3 | ||
|
|
06e88ce062 | ||
|
|
b5201a56df | ||
|
|
9c05f1d8f3 | ||
|
|
093718a1c6 | ||
|
|
c07cb32ef8 | ||
|
|
04da178800 | ||
|
|
5ab8da50cb | ||
|
|
da684a4159 | ||
|
|
ba20bde250 | ||
|
|
cec550ad11 | ||
|
|
62b56e1b56 | ||
|
|
b045846579 | ||
|
|
665aa3c4cf | ||
|
|
0f49a1fe27 | ||
|
|
ad4ab9dc08 | ||
|
|
1293990605 | ||
|
|
c71bf6e8d9 | ||
|
|
716c67d3a5 | ||
|
|
33b1daafa0 | ||
|
|
2532158860 | ||
|
|
da65e347b3 | ||
|
|
c940bc8015 | ||
|
|
9c4676f4c0 | ||
|
|
3b64dae13e | ||
|
|
3126b3efbf | ||
|
|
01e12fa011 | ||
|
|
1d163ee113 | ||
|
|
f97ecd3ba4 | ||
|
|
3d66676c1b | ||
|
|
bf28bab7ba | ||
|
|
a6c2dc89ac | ||
|
|
e36ff3d50e | ||
|
|
dd4c36333b | ||
|
|
9e328ce232 | ||
|
|
624d0994ff | ||
|
|
6ec1ee6072 | ||
|
|
5642788046 | ||
|
|
e8e77bf011 | ||
|
|
964dc6ec87 | ||
|
|
a0d738215a | ||
|
|
9ae1181ca7 | ||
|
|
3b89567d77 | ||
|
|
09bb10377b | ||
|
|
ffbc5519c1 | ||
|
|
c80b634497 | ||
|
|
db6c6573af | ||
|
|
7257dd50c6 | ||
|
|
5f0af48b4c | ||
|
|
41ebe0e779 | ||
|
|
0cc95c947e | ||
|
|
991f66e1a3 | ||
|
|
d75efb9f16 | ||
|
|
3575f00bb4 | ||
|
|
f532221685 | ||
|
|
f502827e88 | ||
|
|
50c842883f | ||
|
|
2f5b25b189 | ||
|
|
fb0fedf8fc | ||
|
|
45e9c12b29 | ||
|
|
e52957fabb | ||
|
|
209f333252 | ||
|
|
aa5c0d14ed | ||
|
|
ca40a3e62f | ||
|
|
8574335fe6 | ||
|
|
9493756895 | ||
|
|
df5e57f381 | ||
|
|
62e05abc77 | ||
|
|
d7d63b09b7 | ||
|
|
ae7b3d87ee | ||
|
|
e2ef08f129 | ||
|
|
8dfa4aa677 | ||
|
|
31522ce703 | ||
|
|
25cad2543d | ||
|
|
34e50d6550 | ||
|
|
b7a06c535d | ||
|
|
bf5b188b13 | ||
|
|
20af487ea9 | ||
|
|
d037976bcd | ||
|
|
330bf8e120 | ||
|
|
7064415265 | ||
|
|
df07350a46 | ||
|
|
66e80820c9 | ||
|
|
5d3173f1b4 | ||
|
|
3b977186ed | ||
|
|
38caafd999 | ||
|
|
d7232dcf56 | ||
|
|
825f73df6b | ||
|
|
696e084b02 | ||
|
|
fcc7306407 | ||
|
|
1f182a25db | ||
|
|
995018f1a7 | ||
|
|
a6fa6f6122 | ||
|
|
1b6462a327 | ||
|
|
6054e93ecc | ||
|
|
3acf03ae1c | ||
|
|
42f4d28715 | ||
|
|
4ee86d8456 | ||
|
|
3b2adc999d | ||
|
|
87ec44d3ee | ||
|
|
182a6d5fe4 | ||
|
|
49b5b5b2b1 | ||
|
|
f984ae4930 | ||
|
|
e6ef506f2f | ||
|
|
2d1ae3d5eb | ||
|
|
d775841457 | ||
|
|
2f60398892 | ||
|
|
393a347bad | ||
|
|
6a4c594792 | ||
|
|
154c16a9d3 | ||
|
|
9536039932 | ||
|
|
cad91fd0d1 | ||
|
|
7615e55e96 | ||
|
|
0f54af1a11 | ||
|
|
a439cf476c | ||
|
|
121e5a0ca8 | ||
|
|
328e53bd7b | ||
|
|
4dd03da2e2 | ||
|
|
ebab67c314 | ||
|
|
5ddab2b9d9 | ||
|
|
265f00becf | ||
|
|
340888e4e2 | ||
|
|
f5bbc895b5 | ||
|
|
57a6c6b238 | ||
|
|
cb10854bc8 | ||
|
|
1796b18284 | ||
|
|
f505050391 | ||
|
|
b0afed4bd0 | ||
|
|
292cbd3d95 | ||
|
|
756e861346 | ||
|
|
6db98b40a1 | ||
|
|
a9852a2d2f | ||
|
|
7302b1544d | ||
|
|
491956a69e | ||
|
|
2b94080ee5 | ||
|
|
4732bd1bf7 | ||
|
|
2bdb44e3ba | ||
|
|
d805c29b73 | ||
|
|
a9187c1f45 | ||
|
|
4e76ae5cc6 | ||
|
|
54b6ee0f27 | ||
|
|
bf8c83e798 | ||
|
|
4aed49b371 | ||
|
|
c1df295845 | ||
|
|
265057be83 | ||
|
|
021fbb6c69 | ||
|
|
843480fdf6 | ||
|
|
3eb78a175e | ||
|
|
de1ae66966 | ||
|
|
ab9a62bcd8 | ||
|
|
41d9e44a23 | ||
|
|
693b85fe40 | ||
|
|
7a90e2a93b | ||
|
|
11c3ea2fdb | ||
|
|
57d604bcb2 | ||
|
|
63e38e40dd | ||
|
|
c66f116449 | ||
|
|
55fb754614 | ||
|
|
afe687e9d7 | ||
|
|
6ca9c77b26 | ||
|
|
990956980b | ||
|
|
f7f30bc5b6 | ||
|
|
81b8d01e96 | ||
|
|
7ecfe45923 | ||
|
|
5212f0c44a | ||
|
|
fa58cedd2e | ||
|
|
8b1b7a0fc9 | ||
|
|
2a37b972d9 | ||
|
|
df7ebd244b | ||
|
|
b4b1cf38a6 | ||
|
|
292d57a60b | ||
|
|
9d6850636a | ||
|
|
8879a76e88 | ||
|
|
0fa38bf0e0 | ||
|
|
36eba38f62 | ||
|
|
fe8d7d78c4 | ||
|
|
ff32d77eba | ||
|
|
84f60ea2be | ||
|
|
cc6fe33709 | ||
|
|
5b3f04c5ff | ||
|
|
dc0bf7bdd8 | ||
|
|
13f21dd942 | ||
|
|
fbe69ae90b | ||
|
|
dedad6a03e | ||
|
|
86d3d2207a | ||
|
|
9c39f76774 | ||
|
|
8c04222a40 | ||
|
|
d84a18e456 | ||
|
|
e42231fab0 | ||
|
|
d60efe04fd | ||
|
|
6e7ca646ec | ||
|
|
cac3e4c62b | ||
|
|
0e323954e4 | ||
|
|
385f5a4379 | ||
|
|
e80d20a740 | ||
|
|
9d4db9738a | ||
|
|
f54859bedf | ||
|
|
556ff733d5 | ||
|
|
4027bfff69 | ||
|
|
dbcc0baa7b | ||
|
|
d45c2aca8a | ||
|
|
fd015edb98 | ||
|
|
5ccb46383c | ||
|
|
fa6fdc4059 |
@@ -18,8 +18,10 @@ Checks: >
|
||||
performance-*,
|
||||
portability-*,
|
||||
readability-*,
|
||||
-readability-else-after-return,
|
||||
-readability-function-cognitive-complexity,
|
||||
-readability-named-parameter,
|
||||
-readability-redundant-member-init,
|
||||
-readability-uppercase-literal-suffix,
|
||||
CheckOptions:
|
||||
- key: cppcoreguidelines-avoid-magic-numbers.IgnoreAllFloatingPointValues
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
name: tools
|
||||
name: analyzer
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- tools
|
||||
- analyzer
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -11,8 +11,8 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
|
||||
env:
|
||||
IWYU: "0.23"
|
||||
LLVM: "19"
|
||||
IWYU: "0.24"
|
||||
LLVM: "20"
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
-DENTT_BUILD_EXAMPLE=ON \
|
||||
-DENTT_BUILD_LIB=ON \
|
||||
-DENTT_BUILD_SNAPSHOT=ON \
|
||||
-DENTT_BUILD_TOOLS=ON \
|
||||
-DENTT_BUILD_TESTBED=ON \
|
||||
-DCMAKE_CXX_INCLUDE_WHAT_YOU_USE="include-what-you-use;-Xiwyu;--mapping_file=${GITHUB_WORKSPACE}/entt.imp;-Xiwyu;--no_fwd_decls;-Xiwyu;--verbose=1" \
|
||||
..
|
||||
make -j4
|
||||
@@ -78,7 +78,7 @@ jobs:
|
||||
-DENTT_BUILD_EXAMPLE=ON \
|
||||
-DENTT_BUILD_LIB=ON \
|
||||
-DENTT_BUILD_SNAPSHOT=ON \
|
||||
-DENTT_BUILD_TOOLS=ON \
|
||||
-DENTT_BUILD_TESTBED=ON \
|
||||
-DENTT_USE_CLANG_TIDY=ON \
|
||||
..
|
||||
make -j4
|
||||
@@ -40,10 +40,12 @@ jobs:
|
||||
windows:
|
||||
strategy:
|
||||
matrix:
|
||||
toolset: [default, v142, clang-cl]
|
||||
toolset: [default, v142, v143, clang-cl]
|
||||
include:
|
||||
- toolset: v142
|
||||
toolset_option: -T"v142"
|
||||
- toolset: v143
|
||||
toolset_option: -T"v143"
|
||||
- toolset: clang-cl
|
||||
toolset_option: -T"ClangCl"
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ name: deploy
|
||||
|
||||
on:
|
||||
release:
|
||||
types: published
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
name: testbed
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
|
||||
linux:
|
||||
timeout-minutes: 15
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install required packages
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y \
|
||||
build-essential \
|
||||
git \
|
||||
make \
|
||||
pkg-config \
|
||||
cmake \
|
||||
ninja-build \
|
||||
gnome-desktop-testing \
|
||||
libasound2-dev \
|
||||
libpulse-dev \
|
||||
libaudio-dev \
|
||||
libjack-dev \
|
||||
libsndio-dev \
|
||||
libx11-dev \
|
||||
libxext-dev \
|
||||
libxrandr-dev \
|
||||
libxcursor-dev \
|
||||
libxfixes-dev \
|
||||
libxi-dev \
|
||||
libxss-dev \
|
||||
libxtst-dev \
|
||||
libxkbcommon-dev \
|
||||
libdrm-dev \
|
||||
libgbm-dev \
|
||||
libgl1-mesa-dev \
|
||||
libgles2-mesa-dev \
|
||||
libegl1-mesa-dev \
|
||||
libdbus-1-dev \
|
||||
libibus-1.0-dev \
|
||||
libudev-dev \
|
||||
libpipewire-0.3-dev \
|
||||
libwayland-dev \
|
||||
libdecor-0-dev \
|
||||
liburing-dev
|
||||
- name: Compile testbed
|
||||
working-directory: build
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTBED=ON ..
|
||||
make -j4
|
||||
|
||||
windows:
|
||||
timeout-minutes: 15
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: seanmiddleditch/gha-setup-ninja@master
|
||||
- name: Compile testbed
|
||||
working-directory: build
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTBED=ON .. -G Ninja
|
||||
cmake --build . -j 4
|
||||
|
||||
macos:
|
||||
timeout-minutes: 15
|
||||
runs-on: macOS-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Compile testbed
|
||||
working-directory: build
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTBED=ON ..
|
||||
make -j4
|
||||
+141
-113
@@ -22,7 +22,7 @@ project(
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
endif()
|
||||
|
||||
|
||||
message(VERBOSE "*")
|
||||
message(VERBOSE "* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
|
||||
message(VERBOSE "* Copyright (c) 2017-2025 Michele Caini <michele.caini@gmail.com>")
|
||||
@@ -105,14 +105,110 @@ if(ENTT_HAS_SANITIZER)
|
||||
endif()
|
||||
|
||||
if(ENTT_CLANG_TIDY_EXECUTABLE)
|
||||
set(CMAKE_CXX_CLANG_TIDY "${ENTT_CLANG_TIDY_EXECUTABLE};--config-file=${EnTT_SOURCE_DIR}/.clang-tidy;--header-filter=${EnTT_SOURCE_DIR}/src/entt/.*")
|
||||
endif()
|
||||
set(ENTT_CLANG_TIDY_OPTIONS ";--config-file=${EnTT_SOURCE_DIR}/.clang-tidy;--header-filter=${EnTT_SOURCE_DIR}/src/entt/.*")
|
||||
|
||||
if(MSVC AND NOT (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang"))
|
||||
set(ENTT_CLANG_TIDY_OPTIONS "${ENTT_CLANG_TIDY_OPTIONS};--extra-arg=/EHsc;--extra-arg=/wd4996")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_CLANG_TIDY "${ENTT_CLANG_TIDY_EXECUTABLE}${ENTT_CLANG_TIDY_OPTIONS}")
|
||||
endif()
|
||||
|
||||
# Add EnTT goodies
|
||||
|
||||
option(ENTT_INCLUDE_HEADERS "Add all EnTT headers to the EnTT target." OFF)
|
||||
option(ENTT_INCLUDE_NATVIS "Add EnTT natvis files to the EnTT target." OFF)
|
||||
|
||||
if(ENTT_INCLUDE_HEADERS)
|
||||
set(
|
||||
HEADERS_FILES
|
||||
config/config.h
|
||||
config/macro.h
|
||||
config/version.h
|
||||
container/dense_map.hpp
|
||||
container/dense_set.hpp
|
||||
container/table.hpp
|
||||
container/fwd.hpp
|
||||
core/algorithm.hpp
|
||||
core/any.hpp
|
||||
core/bit.hpp
|
||||
core/compressed_pair.hpp
|
||||
core/enum.hpp
|
||||
core/family.hpp
|
||||
core/fwd.hpp
|
||||
core/hashed_string.hpp
|
||||
core/ident.hpp
|
||||
core/iterator.hpp
|
||||
core/memory.hpp
|
||||
core/monostate.hpp
|
||||
core/ranges.hpp
|
||||
core/tuple.hpp
|
||||
core/type_info.hpp
|
||||
core/type_traits.hpp
|
||||
core/utility.hpp
|
||||
entity/component.hpp
|
||||
entity/entity.hpp
|
||||
entity/fwd.hpp
|
||||
entity/group.hpp
|
||||
entity/handle.hpp
|
||||
entity/mixin.hpp
|
||||
entity/helper.hpp
|
||||
entity/organizer.hpp
|
||||
entity/ranges.hpp
|
||||
entity/registry.hpp
|
||||
entity/runtime_view.hpp
|
||||
entity/snapshot.hpp
|
||||
entity/sparse_set.hpp
|
||||
entity/storage.hpp
|
||||
entity/view.hpp
|
||||
graph/adjacency_matrix.hpp
|
||||
graph/dot.hpp
|
||||
graph/flow.hpp
|
||||
graph/fwd.hpp
|
||||
locator/locator.hpp
|
||||
meta/adl_pointer.hpp
|
||||
meta/container.hpp
|
||||
meta/context.hpp
|
||||
meta/factory.hpp
|
||||
meta/fwd.hpp
|
||||
meta/meta.hpp
|
||||
meta/node.hpp
|
||||
meta/pointer.hpp
|
||||
meta/policy.hpp
|
||||
meta/range.hpp
|
||||
meta/resolve.hpp
|
||||
meta/template.hpp
|
||||
meta/type_traits.hpp
|
||||
meta/utility.hpp
|
||||
poly/fwd.hpp
|
||||
poly/poly.hpp
|
||||
process/fwd.hpp
|
||||
process/process.hpp
|
||||
process/scheduler.hpp
|
||||
resource/cache.hpp
|
||||
resource/fwd.hpp
|
||||
resource/loader.hpp
|
||||
resource/resource.hpp
|
||||
signal/delegate.hpp
|
||||
signal/dispatcher.hpp
|
||||
signal/emitter.hpp
|
||||
signal/fwd.hpp
|
||||
signal/sigh.hpp
|
||||
tools/davey.hpp
|
||||
entt.hpp
|
||||
fwd.hpp
|
||||
tools.hpp
|
||||
)
|
||||
|
||||
list(TRANSFORM HEADERS_FILES APPEND ">" OUTPUT_VARIABLE HEADERS_BUILD_INTERFACE)
|
||||
list(TRANSFORM HEADERS_BUILD_INTERFACE PREPEND "$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/")
|
||||
|
||||
list(TRANSFORM HEADERS_FILES APPEND ">" OUTPUT_VARIABLE HEADERS_INSTALL_INTERFACE)
|
||||
list(TRANSFORM HEADERS_INSTALL_INTERFACE PREPEND "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/entt/")
|
||||
|
||||
target_sources(EnTT INTERFACE ${HEADERS_BUILD_INTERFACE} ${HEADERS_INSTALL_INTERFACE})
|
||||
endif()
|
||||
|
||||
if(ENTT_INCLUDE_NATVIS)
|
||||
if(MSVC)
|
||||
set(ENTT_HAS_NATVIS TRUE CACHE BOOL "" FORCE)
|
||||
@@ -124,104 +220,29 @@ if(ENTT_INCLUDE_NATVIS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENTT_INCLUDE_HEADERS)
|
||||
target_sources(
|
||||
EnTT
|
||||
INTERFACE
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/config/config.h>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/config/macro.h>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/config/version.h>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/dense_map.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/dense_set.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/table.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/algorithm.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/any.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/attribute.h>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/bit.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/compressed_pair.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/enum.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/family.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/hashed_string.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/ident.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/iterator.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/memory.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/monostate.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/ranges.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/tuple.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/type_info.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/type_traits.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/utility.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/component.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/entity.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/group.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/handle.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/mixin.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/helper.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/organizer.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/ranges.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/registry.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/runtime_view.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/snapshot.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/sparse_set.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/storage.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/view.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/adjacency_matrix.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/dot.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/flow.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/locator/locator.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/adl_pointer.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/container.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/context.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/factory.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/meta.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/node.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/pointer.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/policy.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/range.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/resolve.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/template.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/type_traits.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/utility.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/poly/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/poly/poly.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/process/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/process/process.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/process/scheduler.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/cache.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/loader.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/resource.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/delegate.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/dispatcher.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/emitter.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/fwd.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/sigh.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entt.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/fwd.hpp>
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ENTT_HAS_NATVIS)
|
||||
target_sources(
|
||||
EnTT
|
||||
INTERFACE
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/config.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/container.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/core.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/entity.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/graph.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/locator.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/meta.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/poly.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/process.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/resource.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/signal.natvis>
|
||||
set(
|
||||
NATVIS_FILES
|
||||
config.natvis
|
||||
container.natvis
|
||||
core.natvis
|
||||
entity.natvis
|
||||
graph.natvis
|
||||
locator.natvis
|
||||
meta.natvis
|
||||
poly.natvis
|
||||
process.natvis
|
||||
resource.natvis
|
||||
signal.natvis
|
||||
)
|
||||
|
||||
list(TRANSFORM NATVIS_FILES APPEND ">" OUTPUT_VARIABLE NATVIS_BUILD_INTERFACE)
|
||||
list(TRANSFORM NATVIS_BUILD_INTERFACE PREPEND "$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/natvis/")
|
||||
|
||||
list(TRANSFORM NATVIS_FILES APPEND ">" OUTPUT_VARIABLE NATVIS_INSTALL_INTERFACE)
|
||||
list(TRANSFORM NATVIS_INSTALL_INTERFACE PREPEND "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/entt/natvis/")
|
||||
|
||||
target_sources(EnTT INTERFACE ${NATVIS_BUILD_INTERFACE} ${NATVIS_INSTALL_INTERFACE})
|
||||
endif()
|
||||
|
||||
# Install EnTT and all related files
|
||||
@@ -296,29 +317,36 @@ if(ENTT_INSTALL)
|
||||
FILES_MATCHING
|
||||
PATTERN "*.h"
|
||||
PATTERN "*.hpp"
|
||||
PATTERN "*.natvis"
|
||||
)
|
||||
|
||||
export(PACKAGE EnTT)
|
||||
endif()
|
||||
|
||||
# Tests
|
||||
# Tests and testbed
|
||||
|
||||
option(ENTT_BUILD_TESTING "Enable building tests." OFF)
|
||||
option(ENTT_BUILD_TESTBED "Enable building testbed." OFF)
|
||||
|
||||
if(ENTT_BUILD_TESTING)
|
||||
option(ENTT_FIND_GTEST_PACKAGE "Enable finding gtest package." OFF)
|
||||
if(ENTT_BUILD_TESTING OR ENTT_BUILD_TESTBED)
|
||||
set(ENTT_ID_TYPE std::uint32_t CACHE STRING "Type of identifiers to use for tests and testbed")
|
||||
set(ENTT_CXX_STD cxx_std_17 CACHE STRING "C++ standard revision to use for tests and testbed")
|
||||
|
||||
option(ENTT_BUILD_BENCHMARK "Build benchmark." OFF)
|
||||
option(ENTT_BUILD_EXAMPLE "Build examples." OFF)
|
||||
option(ENTT_BUILD_LIB "Build lib tests." OFF)
|
||||
option(ENTT_BUILD_SNAPSHOT "Build snapshot test with Cereal." OFF)
|
||||
|
||||
set(ENTT_ID_TYPE std::uint32_t CACHE STRING "Type of identifiers to use for the tests")
|
||||
set(ENTT_CXX_STD cxx_std_17 CACHE STRING "C++ standard revision to use for the tests")
|
||||
|
||||
include(CTest)
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
# Tests and tesetbed do not work together because SDL gets confused with EnTT tests
|
||||
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)
|
||||
elseif(ENTT_BUILD_TESTBED)
|
||||
add_subdirectory(testbed)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Documentation
|
||||
|
||||
@@ -232,7 +232,8 @@ build system of the library.
|
||||
## CMake
|
||||
|
||||
To use `EnTT` from a `CMake` project, just link an existing target to the
|
||||
`EnTT::EnTT` alias.<br/>
|
||||
`EnTT::EnTT` alias.
|
||||
|
||||
The library offers everything you need for locating (as in `find_package`),
|
||||
embedding (as in `add_subdirectory`), fetching (as in `FetchContent`) or using
|
||||
it in many of the ways that you can think of and that involve `CMake`.<br/>
|
||||
@@ -240,12 +241,17 @@ Covering all possible cases would require a treatise and not a simple README
|
||||
file, but I'm confident that anyone reading this section also knows what it's
|
||||
about and can use `EnTT` from a `CMake` project without problems.
|
||||
|
||||
Note that all `install` calls are guarded by the `ENTT_INSTALL` option to allow
|
||||
using `EnTT` as a submodule without conflicting with user logic.<br/>
|
||||
It is therefore necessary to set the option to true to take advantage of the
|
||||
installation logic provided by this library.
|
||||
|
||||
## Natvis support
|
||||
|
||||
When using `CMake`, just enable the option `ENTT_INCLUDE_NATVIS` and enjoy
|
||||
it.<br/>
|
||||
Otherwise, most of the tools are covered via Natvis and all files can be found
|
||||
in the `natvis` directory, divided by module.<br/>
|
||||
in the `natvis` subdirectory, divided by module.<br/>
|
||||
If you spot errors or have suggestions, any contribution is welcome!
|
||||
|
||||
## Packaging Tools
|
||||
|
||||
@@ -13,19 +13,13 @@ TODO:
|
||||
* review all NOLINT
|
||||
* bring nested groups back in place (see bd34e7f)
|
||||
* work stealing job system (see #100) + mt scheduler based on const awareness for types
|
||||
* view: reduce inst due to/improve perf with index-based approach in dispatch_get/pick_and_each/each (single type too, define storage ::at and ::at_as_tuple)
|
||||
* view: update natvis as needed after the last rework, merge pools/filter in the same array, drop check (?) and turn view into a position
|
||||
* view: type-only view_iterator (dyn get/excl sizes), type-only basic_common_view (dyn get/excl sizes with pointer to array from derived)
|
||||
* combine version-mask-vs-version-bits tricks with reserved bits to allow things like enabling/disabling
|
||||
* self contained entity traits to avoid explicit specializations (ie enum constants)
|
||||
* auto type info data from types if present
|
||||
* test: push sharing types further
|
||||
* storage entity: fast range-push from above
|
||||
* table: pop back to support swap and pop, single column access, empty type optimization
|
||||
* review cmake warning about FetchContent_Populate (need .28 and EXCLUDE_FROM_ALL for FetchContent)
|
||||
* suppress -Wself-move on CI with g++13
|
||||
* view specializations for multi, single and filtered elements
|
||||
* don't pass reactive storage by default to callback
|
||||
* runtime types support for meta for types that aren't backed by C++ types
|
||||
* built-in no-pagination storage - no_pagination page size as limits::max
|
||||
* any cdynamic to support const ownership construction
|
||||
@@ -33,5 +27,11 @@ TODO:
|
||||
* FetchContent_Populate -> FetchContent_MakeAvailable warnings
|
||||
* doc: IMPLICIT_DIR_DOCS for dir docs or \dir
|
||||
* meta non-const allow_cast overloads: (const int &) to (int &) is not allowed, but (const int &) to (double &) is allowed (support only for convertibles)
|
||||
* improve non-const allow cast with in-place switch
|
||||
* meta fixed_size could return the size directly if present
|
||||
* review build process for testbed (i.e. tests first due to SDL)
|
||||
* use unique_ptr or any for meta_custom_node
|
||||
* paged vector as a standalone class
|
||||
* resource: shared_from_this?
|
||||
* finish the imgui viewer/editor!
|
||||
* archetype-like a-là EnTT support (see my own notes)
|
||||
* meta: conversion_helper machinery has lot of room for improvements
|
||||
* organizer: view/storage only based model, no registry
|
||||
|
||||
+3
-2
@@ -45,8 +45,9 @@ also limited to this library only.
|
||||
In general, `EnTT` does not 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 one who knows if a synchronization point is required.<br/>
|
||||
However, some features are not easily accessible to users and are made
|
||||
thread-safe by means of this definition.
|
||||
However, some internal static data shared between threads should be atomic when
|
||||
using `EnTT` from multiple threads, even when dealing with local storage. Define
|
||||
this macro without assigning any value to it to get the job done.
|
||||
|
||||
## ENTT_ID_TYPE
|
||||
|
||||
|
||||
+4
-4
@@ -466,7 +466,7 @@ The `allocate_unique` function follows this proposal, making a virtue out of
|
||||
necessity:
|
||||
|
||||
```cpp
|
||||
std::unique_ptr<my_type, entt::allocation_deleter<my_type>> ptr = entt::allocate_unique<my_type>(allocator, arguments);
|
||||
std::unique_ptr<my_type, entt::allocation_deleter<allocator_type>> ptr = entt::allocate_unique<my_type>(allocator, arguments);
|
||||
```
|
||||
|
||||
Although the internal implementation is slightly different from what is proposed
|
||||
@@ -639,7 +639,7 @@ These are the information made available by `type_info`:
|
||||
This is also an alias for the following:
|
||||
|
||||
```cpp
|
||||
auto idx = entt::type_index<std::remove_cv_t<std::remove_reference_t<a_type>>>::value();
|
||||
auto idx = entt::type_index<std::remove_const_t<std::remove_reference_t<a_type>>>::value();
|
||||
```
|
||||
|
||||
* The hash value associated with a given type:
|
||||
@@ -651,7 +651,7 @@ These are the information made available by `type_info`:
|
||||
This is also an alias for the following:
|
||||
|
||||
```cpp
|
||||
auto hash = entt::type_hash<std::remove_cv_t<std::remove_reference_t<a_type>>>::value();
|
||||
auto hash = entt::type_hash<std::remove_const_t<std::remove_reference_t<a_type>>>::value();
|
||||
```
|
||||
|
||||
* The name associated with a given type:
|
||||
@@ -663,7 +663,7 @@ These are the information made available by `type_info`:
|
||||
This is also an alias for the following:
|
||||
|
||||
```cpp
|
||||
auto name = entt::type_name<std::remove_cv_t<std::remove_reference_t<a_type>>>::value();
|
||||
auto name = entt::type_name<std::remove_const_t<std::remove_reference_t<a_type>>>::value();
|
||||
```
|
||||
|
||||
Where all accessed features are available at compile-time, the `type_info` class
|
||||
|
||||
+29
-7
@@ -11,6 +11,7 @@
|
||||
* [Vademecum](#vademecum)
|
||||
* [The Registry, the Entity and the Component](#the-registry-the-entity-and-the-component)
|
||||
* [Observe changes](#observe-changes)
|
||||
* [Auto-binding](#auto-binding)
|
||||
* [Entity lifecycle](#entity-lifecycle)
|
||||
* [Listeners disconnection](#listeners-disconnection)
|
||||
* [They call me reactive storage](#they-call-me-reactive-storage)
|
||||
@@ -409,6 +410,27 @@ There are many useful but less known functionalities that are not described
|
||||
here, such as the connection objects or the possibility to attach listeners with
|
||||
a list of parameters that is shorter than that of the signal itself.
|
||||
|
||||
### Auto-binding
|
||||
|
||||
Users don't need to create bindings manually each and every time. For managed
|
||||
types, they can have `EnTT` setup listeners automatically.<br/>
|
||||
The library searches the types for functions with specific names and signatures,
|
||||
as in the following example:
|
||||
|
||||
```cpp
|
||||
struct my_type {
|
||||
static void on_construct(entt::registry ®istry, const entt::entity entt);
|
||||
static void on_update(entt::registry ®istry, const entt::entity entt);
|
||||
static void on_destroy(entt::registry ®istry, const entt::entity entt);
|
||||
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
As soon as a storage is created for such a defined type, these functions are
|
||||
associated with the respective signals. The function name is self-explanatory of
|
||||
the target signal.
|
||||
|
||||
### Entity lifecycle
|
||||
|
||||
Observing entities is also possible. In this case, the user must use the entity
|
||||
@@ -1548,7 +1570,7 @@ is not known), there is always the possibility of receiving a `type_info` object
|
||||
for the type of elements associated with the entities (if any):
|
||||
|
||||
```cpp
|
||||
if(entt::type_id<velocity>() == base.type()) {
|
||||
if(entt::type_id<velocity>() == base.info()) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
@@ -2134,13 +2156,13 @@ It means that views and groups generated by a const registry also propagate the
|
||||
constness to the types involved. As an example:
|
||||
|
||||
```cpp
|
||||
entt::view<const position, const velocity> view = std::as_const(registry).view<const position, const velocity>();
|
||||
entt::view<entt::get_t<const position, const velocity>> view = std::as_const(registry).view<const position, const velocity>();
|
||||
```
|
||||
|
||||
Consider the following definition for a non-const view instead:
|
||||
|
||||
```cpp
|
||||
entt::view<position, const velocity> view = registry.view<position, const velocity>();
|
||||
entt::view<entt::get_t<position, const velocity>> view = registry.view<position, const velocity>();
|
||||
```
|
||||
|
||||
In the example above, `view` is used to access either read-only or writable
|
||||
@@ -2345,10 +2367,10 @@ expedients.
|
||||
Finally, `EnTT` is configured via a few compile-time definitions to make some of
|
||||
its parts implicitly thread-safe, roughly speaking only the ones that really
|
||||
make sense and cannot be turned around.<br/>
|
||||
In particular, when multiple instances of objects referencing the type index
|
||||
generator (such as the `registry` class) are used in different threads, then it
|
||||
might be useful to define `ENTT_USE_ATOMIC`.<br/>
|
||||
See the relevant documentation for more information.
|
||||
When using multiple threads with `EnTT`, you should define `ENTT_USE_ATOMIC`
|
||||
unless you know exactly what you are doing. This is true even if each thread
|
||||
only uses thread local data. For more information, see
|
||||
[this section](config.md#entt_use_atomic).
|
||||
|
||||
## Iterators
|
||||
|
||||
|
||||
+2
-2
@@ -28,8 +28,8 @@ The section dedicated to `type_info` contains all the details to get around the
|
||||
issue in a concise and elegant way. Please refer to the specific documentation.
|
||||
|
||||
When working with linked libraries, compile definitions `ENTT_API_EXPORT` and
|
||||
`ENTT_API_IMPORT` are to import or export symbols, so as to make everything work
|
||||
nicely across boundaries.<br/>
|
||||
`ENTT_API_IMPORT` are there to import or export symbols, so as to make
|
||||
everything work nicely across boundaries.<br/>
|
||||
On the other hand, everything should run smoothly when working with plugins or
|
||||
shared libraries that do not export any symbols.
|
||||
|
||||
|
||||
@@ -141,6 +141,10 @@ I hope the following lists can grow much more in the future.
|
||||
small demo game with ships and bullets flying everywhere on the screen.
|
||||
* [Lichgate](https://buas.itch.io/lichgate): step into the robes of a powerful
|
||||
mage determined to halt the relentless hordes of undead.
|
||||
* [You Are Circle](https://store.steampowered.com/app/3578190/You_Are_Circle/):
|
||||
a roguelite top-down shooter with a high-contrast vector line aesthetic.
|
||||
* [EnTT Dino](https://github.com/omgitsaheadcrab/entt_dino): a Dinosaur Game
|
||||
clone in C++ using only `SDL2` and `EnTT`.
|
||||
|
||||
## Engines and the like:
|
||||
|
||||
@@ -241,6 +245,8 @@ I hope the following lists can grow much more in the future.
|
||||
* [Darmok](https://github.com/miguelibero/darmok): another C++ game engine.
|
||||
* [Magique](https://github.com/gk646/magique): 2D game engine for programmers
|
||||
(or those yet to be).
|
||||
* [Physecs](https://github.com/thfProjects/Physecs): real-time 3D rigid body
|
||||
physics simulation built on `EnTT`.
|
||||
|
||||
## Articles, videos and blog posts:
|
||||
|
||||
@@ -321,6 +327,9 @@ I hope the following lists can grow much more in the future.
|
||||
[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).
|
||||
* [OneArc](https://onearc.com/): [licenses](https://onearc.com/third-party-licensors/)
|
||||
do not lie. Their products use EnTT in some way, but it is not known _what_
|
||||
way.
|
||||
* [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
|
||||
|
||||
+72
-40
@@ -3,10 +3,11 @@
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Names and identifiers](#names-and-identifiers)
|
||||
* [Identifiers](#identifiers)
|
||||
* [Reflection in a nutshell](#reflection-in-a-nutshell)
|
||||
* [Any to the rescue](#any-to-the-rescue)
|
||||
* [Enjoy the runtime](#enjoy-the-runtime)
|
||||
* [Tell me your name](#tell-me-your-name)
|
||||
* [Container support](#container-support)
|
||||
* [Pointer-like types](#pointer-like-types)
|
||||
* [Template information](#template-information)
|
||||
@@ -33,19 +34,19 @@ reflection system for `EnTT`. Maybe I did not 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
|
||||
# Identifiers
|
||||
|
||||
The meta system does not 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/>
|
||||
library when it comes to working with identifiers. It does this by offering an
|
||||
API that works with integral values 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 does not 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 is likely that a user defined literal is used as
|
||||
That being said, some of the examples in the following sections are based on the
|
||||
`hashed_string` class as provided by this library. Therefore, where an integral
|
||||
identifier is provided, it is likely that a user defined literal is used as
|
||||
follows:
|
||||
|
||||
```cpp
|
||||
@@ -82,8 +83,8 @@ However, it is also possible to assign custom identifiers to meta types:
|
||||
entt::meta_factory<my_type>{}.type("reflected_type"_hs);
|
||||
```
|
||||
|
||||
Identifiers are used to _retrieve_ meta types at runtime by _name_ other than by
|
||||
type.<br/>
|
||||
Identifiers are used instead of the type to _retrieve_ meta types at runtime, if
|
||||
necessary.<br/>
|
||||
However, users can be interested in adding features to a reflected type so that
|
||||
the reflection system can use it correctly under the hood, while they do not
|
||||
want to also make the type _searchable_. In this case, it is sufficient not to
|
||||
@@ -103,17 +104,6 @@ generally used to create the following:
|
||||
|
||||
Meta default constructors are implicitly generated, if possible.
|
||||
|
||||
* _Destructors_. Both free functions and member functions are valid destructors:
|
||||
|
||||
```cpp
|
||||
entt::meta_factory<my_type>{}.dtor<&destroy>();
|
||||
```
|
||||
|
||||
The purpose is to offer the possibility to free up resources that require
|
||||
_special treatment_ before an object is actually destroyed.<br/>
|
||||
A function should neither delete nor explicitly invoke the destructor of a
|
||||
given instance.
|
||||
|
||||
* _Data members_. Meta data members are actual data members of the underlying
|
||||
type but also static and global variables or constants of any kind. From the
|
||||
point of view of the client, all the variables associated with the reflected
|
||||
@@ -127,7 +117,7 @@ generally used to create the following:
|
||||
```
|
||||
|
||||
The `data` function requires the identifier to use for the meta data member.
|
||||
Users can then access it by _name_ at runtime.<br/>
|
||||
This is then used for runtime access.<br/>
|
||||
Data members are also defined by means of a setter and getter pair. These are
|
||||
either free functions, class members or a mix of them. This approach is also
|
||||
convenient to create read-only properties from a non-const data member:
|
||||
@@ -149,7 +139,7 @@ generally used to create the following:
|
||||
```
|
||||
|
||||
The `func` function requires the identifier to use for the meta data function.
|
||||
Users can then access it by _name_ at runtime.<br/>
|
||||
This is then used for runtime access.<br/>
|
||||
Overloading of meta functions is supported. Overloaded functions are resolved
|
||||
at runtime by the reflection system according to the types of the arguments.
|
||||
|
||||
@@ -240,7 +230,7 @@ In all cases, the returned value is an instance of `meta_type` (possibly with
|
||||
its id). These objects offer an API to know their _runtime identifiers_, to
|
||||
iterate all the meta objects associated with them and even to build instances of
|
||||
the underlying type.<br/>
|
||||
Meta data members and functions are accessed by name:
|
||||
Meta data members and functions are accessed by means of their identifiers:
|
||||
|
||||
* Meta data members:
|
||||
|
||||
@@ -268,6 +258,9 @@ Meta data members and functions are accessed by name:
|
||||
addition, a meta function object is used to invoke the underlying function and
|
||||
then get the return value in the form of a `meta_any` object.
|
||||
|
||||
Both functions search for the elements throughout the meta type hierarchy.
|
||||
However, they offer the option of passing a second boolean argument to stop the
|
||||
search at the top-level meta type.<br/>
|
||||
All the meta objects thus obtained as well as the meta types explicitly convert
|
||||
to a boolean value to check for validity:
|
||||
|
||||
@@ -292,19 +285,49 @@ 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 was found
|
||||
or not.
|
||||
|
||||
There is no object that wraps the destructor of a meta type nor a `destroy`
|
||||
member function in its API. Destructors are invoked implicitly by `meta_any`
|
||||
behind the scenes and users have not to deal with them explicitly. Furthermore,
|
||||
they have no name, cannot be searched and would not have member functions to
|
||||
expose anyway.<br/>
|
||||
Similarly, conversion functions are not directly accessible. They are used
|
||||
internally by `meta_any` and the meta objects when needed.
|
||||
or not.<br/>
|
||||
Conversion functions are not accessible instead. 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 was said.
|
||||
Refer to the inline documentation for further details.
|
||||
|
||||
### Tell me your name
|
||||
|
||||
For meta types, data and functions, users can also provide custom _names_:
|
||||
|
||||
```cpp
|
||||
entt::meta_factory<my_type>{}
|
||||
.type("type"_hs, "my_type")
|
||||
.data<&variable>("data"_hs, "variable")
|
||||
.func<&function>("func"_hs, "function");
|
||||
```
|
||||
|
||||
The _label_ provided **should** be a string literal. The library does not make
|
||||
copies. It is up to the user to guarantee the lifetime of the name itself.<br/>
|
||||
String identifiers are returned from the meta objects via the `name` function:
|
||||
|
||||
```cpp
|
||||
const char *name = entt::resolve<my_type>().name();
|
||||
```
|
||||
|
||||
Since most of the time there is no need to differentiate the _name_ from the
|
||||
numeric identifier associated with a meta object, `EnTT` also offers a more
|
||||
compact version of these functions:
|
||||
|
||||
```cpp
|
||||
entt::meta_factory<my_type>{}
|
||||
.type("my_type")
|
||||
.data<&variable>("variable")
|
||||
.func<&function>("function");
|
||||
```
|
||||
|
||||
Again, the name provided **should** be a string literal. The string is then used
|
||||
to generate a numeric identifier with the `hashed_string` class.<br/>
|
||||
Despite support for names, there are no string-based lookup functions available.
|
||||
That is, types (`resolve`) as well as data members (`data`) and function members
|
||||
(`func`) are only _searchable_ by numeric identifiers.
|
||||
|
||||
## Container support
|
||||
|
||||
The runtime reflection system also supports containers of all types.<br/>
|
||||
@@ -767,7 +790,7 @@ other problems.
|
||||
|
||||
There are a few alternatives available at the moment:
|
||||
|
||||
* The _as-is_ policy, associated with the type `entt::as_is_t`.<br/>
|
||||
* The _as-value_ policy, associated with the type `entt::as_value_t`.<br/>
|
||||
This is the default policy. In general, it should never be used explicitly,
|
||||
since it is implicitly selected if no other policy is specified.<br/>
|
||||
In this case, the return values of the functions as well as the properties
|
||||
@@ -805,6 +828,18 @@ There are a few alternatives available at the moment:
|
||||
`as_ref_t` _adapts_ to the constness of the passed object and to that of the
|
||||
return type if any.
|
||||
|
||||
* The _as-is_ policy, associated with the type `entt::as_is_t`.<br/>
|
||||
Useful for decoupling meta type creation code from calling code while still
|
||||
preserving the behavior of data members and member functions as defined:
|
||||
|
||||
```cpp
|
||||
entt::meta_factory<my_type>{}.func<&my_type::any_member, entt::as_is_t>("member"_hs);
|
||||
```
|
||||
|
||||
For data members or member functions that return a reference type, the value
|
||||
is returned by reference with the same constness. In all other cases, the
|
||||
value is returned by copy.
|
||||
|
||||
Some uses are rather trivial, but it is useful to note that there are some less
|
||||
obvious corner cases that can in turn be solved with the use of policies.
|
||||
|
||||
@@ -867,12 +902,9 @@ entt::meta_factory<my_type>{}.traits(my_traits::required | my_traits::hidden);
|
||||
|
||||
In the example above, `EnTT` bitmask enum support is used, but any integral
|
||||
value is fine, as long as it does not exceed 16 bits.<br/>
|
||||
It is not possible to assign traits at different times. Therefore, multiple
|
||||
calls to the `traits` function overwrite previous values. However, traits can be
|
||||
read from meta objects and used to update existing data with a factory,
|
||||
effectively extending them as needed.<br/>
|
||||
Likewise, users can also set traits on meta objects later if needed, as long as
|
||||
the factory is reset to the meta object of interest:
|
||||
Traits can be assigned at different times. Subsequent calls to the `traits`
|
||||
function do not reset previously set values. However, users must reset the
|
||||
factory to the meta object of interest:
|
||||
|
||||
```cpp
|
||||
entt::meta_factory<my_type>{}
|
||||
|
||||
+75
-62
@@ -4,7 +4,8 @@
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [The process](#the-process)
|
||||
* [Adaptor](#adaptor)
|
||||
* [Continuation](#continuation)
|
||||
* [Shared process](#shared-process)
|
||||
* [The scheduler](#the-scheduler)
|
||||
|
||||
# Introduction
|
||||
@@ -17,28 +18,20 @@ to define and execute cooperative processes.
|
||||
|
||||
# The process
|
||||
|
||||
A typical task inherits from the `process` class template that stays true to the
|
||||
CRTP idiom. Moreover, derived classes specify what the intended type for elapsed
|
||||
times is.
|
||||
A typical task inherits from the `process` class template. Derived classes also
|
||||
specify what the intended type for elapsed times is.
|
||||
|
||||
A process should expose publicly the following member functions whether needed
|
||||
(note that it is not required to define a function unless the derived class
|
||||
wants to _override_ the default behavior):
|
||||
A process should implement the following member functions whether needed (note
|
||||
that it is not required to define a function unless the derived class wants to
|
||||
_override_ the default behavior):
|
||||
|
||||
* `void update(Delta, void *);`
|
||||
|
||||
This is invoked once per tick until a process is explicitly aborted or ends
|
||||
either with or without errors. Technically speaking, this member function is
|
||||
not strictly required. However, each process should at least define it to work
|
||||
either with or without errors. 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();`
|
||||
|
||||
This is invoked when the process joins the running queue of a scheduler. It
|
||||
happens usually when the process is attached to the scheduler if it is a top
|
||||
level one, or when it replaces its parent if it is a _continuation_.
|
||||
|
||||
* `void succeeded();`
|
||||
|
||||
This is invoked in case of success, immediately after an update and during the
|
||||
@@ -52,22 +45,24 @@ wants to _override_ the default behavior):
|
||||
* `void aborted();`
|
||||
|
||||
This is invoked only if a process is explicitly aborted. There is no guarantee
|
||||
that it executes in the same tick, it depends solely on whether the process is
|
||||
that it executes in the same tick. It depends solely on whether the process is
|
||||
aborted immediately or not.
|
||||
|
||||
Derived classes can also change the internal state of a process by invoking
|
||||
`succeed` and `fail`, as well as `pause` and `unpause` the process itself.<br/>
|
||||
All these are protected member functions made available to manage the life cycle
|
||||
of a process from a derived class.
|
||||
A class can also change the state of a process by invoking `succeed` and `fail`,
|
||||
as well as `pause` and `unpause` the process itself.<br/>
|
||||
All these are public member functions made available to manage the life cycle of
|
||||
a process easily.
|
||||
|
||||
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;
|
||||
struct my_process: entt::process {
|
||||
using allocator_type = typename entt::process::allocator_type;
|
||||
using delta_type = typename entt::process::delta_type;
|
||||
|
||||
my_process(delta_type delay)
|
||||
: remaining{delay}
|
||||
my_process(const allocator_type &allocator, delta_type delay)
|
||||
: entt::process{allocator},
|
||||
remaining{delay}
|
||||
{}
|
||||
|
||||
void update(delta_type delta, void *) {
|
||||
@@ -85,33 +80,49 @@ private:
|
||||
};
|
||||
```
|
||||
|
||||
## Adaptor
|
||||
## Continuation
|
||||
|
||||
Lambdas and functors cannot be used directly with a scheduler because 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 callbacks to
|
||||
invoke whenever a process terminates with success or with an error:
|
||||
A process may be followed by other processes upon successful termination.<br/>
|
||||
This pairing can be set up at creation time, keeping the processes conceptually
|
||||
separate from each other while still combining them at runtime:
|
||||
|
||||
```cpp
|
||||
void(Delta delta, void *data, auto succeed, auto fail);
|
||||
my_process process{};
|
||||
process.then<my_other_process>();
|
||||
```
|
||||
|
||||
Parameters have the following meaning:
|
||||
This approach allows processes to be developed in isolation and combined to
|
||||
define complex actions.<br/>
|
||||
For example, a delayed operation where a parent process (such as a timer)
|
||||
_schedules_ a child process (the deferred task) once the time is over.
|
||||
|
||||
* `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.
|
||||
The `then` function also accepts lambdas, which are associated with a dedicated
|
||||
process internally:
|
||||
|
||||
Both `succeed` and `fail` accept no parameters at all.
|
||||
```cpp
|
||||
process.then([](entt::process &proc, std::uint32_t delta, void *data) {
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
Note that usually users should not 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 lambda function is such that it accepts a reference to the process that
|
||||
manages it (to be able to terminate it, pause it and so on), plus the usual
|
||||
values also passed to the `update` function.
|
||||
|
||||
## Shared process
|
||||
|
||||
All processes inherit from `std::enable_shared_from_this` to allow sharing with
|
||||
the caller.<br/>
|
||||
The returned smart pointer was created using the allocator associated with the
|
||||
scheduler and therefore all its processes. This same allocator is available by
|
||||
invoking `get_allocator` on the process itself.
|
||||
|
||||
As far as possible, sharing a process is not intended to allow the caller to
|
||||
manage it. This could actually compromise the proper functioning of the
|
||||
scheduler and the process itself.<br/>
|
||||
Rather, the purpose is to allow the callers to save a valid reference to the
|
||||
process, allowing them to intervene in its lifecycle through calls like `pause`
|
||||
and the like.
|
||||
|
||||
# The scheduler
|
||||
|
||||
@@ -124,7 +135,7 @@ it is 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 is
|
||||
easy to create a chain of processes to run sequentially.
|
||||
easy to create a _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:
|
||||
@@ -137,7 +148,7 @@ Otherwise, the `scheduler` alias is also available for the most common cases. It
|
||||
uses `std::uint32_t` as a default type:
|
||||
|
||||
```cpp
|
||||
entt::scheduler scheduler;
|
||||
entt::scheduler scheduler{};
|
||||
```
|
||||
|
||||
The class has member functions to query its internal data structures, like
|
||||
@@ -154,34 +165,33 @@ entt::scheduler::size_type size = scheduler.size();
|
||||
scheduler.clear();
|
||||
```
|
||||
|
||||
To attach a process to a scheduler, there are mainly two ways:
|
||||
To attach a process to a scheduler, invoke the `attach` function with a process
|
||||
type and the arguments to use to construct it:
|
||||
|
||||
* If the process inherits from the `process` class template, it is 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);
|
||||
```
|
||||
|
||||
```cpp
|
||||
scheduler.attach<my_process>(1000u);
|
||||
```
|
||||
The scheduler will also provide the process with its allocator as the first
|
||||
argument.<br>
|
||||
In case of lambdas or functors, the required signature is the one already seen
|
||||
for the `then` function of a process:
|
||||
|
||||
* Otherwise, in case of a lambda or a functor, it is enough to provide an
|
||||
instance of the class to the `attach` member function:
|
||||
```cpp
|
||||
scheduler.attach([](entt::process &, std::uint32_t, void *){ /* ... */ });
|
||||
```
|
||||
|
||||
```cpp
|
||||
scheduler.attach([](auto...){ /* ... */ });
|
||||
```
|
||||
|
||||
In both cases, the scheduler is returned and its `then` member function can be
|
||||
used to create chains of processes to run sequentially.<br/>
|
||||
In both cases, the newly created process is returned by reference and its `then`
|
||||
member function is used 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) {
|
||||
scheduler.attach([](entt::process &, std::uint32_t, void *) {
|
||||
// ...
|
||||
})
|
||||
// appends a child in the form of another lambda function
|
||||
.then([](auto delta, void *, auto succeed, auto fail) {
|
||||
.then([](entt::process &, std::uint32_t, void *) {
|
||||
// ...
|
||||
})
|
||||
// appends a child in the form of a process class
|
||||
@@ -209,3 +219,6 @@ scheduler.abort(true);
|
||||
// ... or gracefully during the next tick
|
||||
scheduler.abort();
|
||||
```
|
||||
|
||||
The argument passed to the `abort` function indicates whether execution should
|
||||
be stopped immediately or processes should be notified on the next tick.
|
||||
|
||||
+2
-2
@@ -112,13 +112,13 @@ struct my_loader {
|
||||
struct from_disk_tag{};
|
||||
struct from_network_tag{};
|
||||
|
||||
template<typename Args>
|
||||
template<typename... Args>
|
||||
result_type operator()(from_disk_tag, Args&&... args) {
|
||||
// ...
|
||||
return std::make_shared<my_resource>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename Args>
|
||||
template<typename... Args>
|
||||
result_type operator()(from_network_tag, Args&&... args) {
|
||||
// ...
|
||||
return std::make_shared<my_resource>(std::forward<Args>(args)...);
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
</AutoVisualizer>
|
||||
+7190
-5250
File diff suppressed because it is too large
Load Diff
@@ -95,6 +95,32 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
#if defined _MSC_VER
|
||||
# pragma detect_mismatch("entt.version", ENTT_VERSION)
|
||||
# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*)
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 15
|
||||
#define ENTT_VERSION_MINOR 16
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
|
||||
#define ENTT_VERSION \
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace entt {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
static constexpr std::size_t dense_map_placeholder_position = (std::numeric_limits<std::size_t>::max)();
|
||||
|
||||
template<typename Key, typename Type>
|
||||
struct dense_map_node final {
|
||||
using value_type = std::pair<Key, Type>;
|
||||
@@ -190,9 +192,7 @@ public:
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::forward_iterator_tag;
|
||||
|
||||
constexpr dense_map_local_iterator() noexcept
|
||||
: it{},
|
||||
offset{} {}
|
||||
constexpr dense_map_local_iterator() noexcept = default;
|
||||
|
||||
constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept
|
||||
: it{iter},
|
||||
@@ -226,8 +226,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
It it;
|
||||
std::size_t offset;
|
||||
It it{};
|
||||
std::size_t offset{dense_map_placeholder_position};
|
||||
};
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
@@ -260,6 +260,7 @@ template<typename Key, typename Type, typename Hash, typename KeyEqual, typename
|
||||
class dense_map {
|
||||
static constexpr float default_threshold = 0.875f;
|
||||
static constexpr std::size_t minimum_capacity = 8u;
|
||||
static constexpr std::size_t placeholder_position = internal::dense_map_placeholder_position;
|
||||
|
||||
using node_type = internal::dense_map_node<Key, Type>;
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
@@ -274,10 +275,10 @@ class dense_map {
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) {
|
||||
for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
|
||||
if(packed.second()(it->first, key)) {
|
||||
return begin() + static_cast<difference_type>(it.index());
|
||||
[[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) {
|
||||
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) {
|
||||
if(packed.second()(packed.first()[offset].element.first, key)) {
|
||||
return begin() + static_cast<typename iterator::difference_type>(offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,10 +286,10 @@ class dense_map {
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const {
|
||||
for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
|
||||
if(packed.second()(it->first, key)) {
|
||||
return cbegin() + static_cast<difference_type>(it.index());
|
||||
[[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) const {
|
||||
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) {
|
||||
if(packed.second()(packed.first()[offset].element.first, key)) {
|
||||
return cbegin() + static_cast<typename const_iterator::difference_type>(offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -695,7 +696,7 @@ public:
|
||||
* @return Number of elements removed (either 0 or 1).
|
||||
*/
|
||||
size_type erase(const key_type &key) {
|
||||
for(size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) {
|
||||
for(size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != placeholder_position; curr = &packed.first()[*curr].next) {
|
||||
if(packed.second()(packed.first()[*curr].element.first, key)) {
|
||||
const auto index = *curr;
|
||||
*curr = packed.first()[*curr].next;
|
||||
@@ -725,6 +726,29 @@ public:
|
||||
return it->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Accesses a given element with bounds checking.
|
||||
* @tparam Other Type of the key of an element to find.
|
||||
* @param key A key of an element to find.
|
||||
* @return A reference to the mapped value of the requested element.
|
||||
*/
|
||||
template<typename Other>
|
||||
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, mapped_type const &>>
|
||||
at(const Other &key) const {
|
||||
auto it = find(key);
|
||||
ENTT_ASSERT(it != cend(), "Invalid key");
|
||||
return it->second;
|
||||
}
|
||||
|
||||
/*! @copydoc at */
|
||||
template<typename Other>
|
||||
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, mapped_type &>>
|
||||
at(const Other &key) {
|
||||
auto it = find(key);
|
||||
ENTT_ASSERT(it != end(), "Invalid key");
|
||||
return it->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Accesses or inserts a given element.
|
||||
* @param key A key of an element to find or insert.
|
||||
@@ -895,7 +919,7 @@ public:
|
||||
* @return An iterator to the end of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
|
||||
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -913,7 +937,7 @@ public:
|
||||
* @return An iterator to the end of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
|
||||
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -990,7 +1014,7 @@ public:
|
||||
sparse.first().resize(sz);
|
||||
|
||||
for(auto &&elem: sparse.first()) {
|
||||
elem = (std::numeric_limits<size_type>::max)();
|
||||
elem = placeholder_position;
|
||||
}
|
||||
|
||||
for(size_type pos{}, last = size(); pos < last; ++pos) {
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace entt {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
static constexpr std::size_t dense_set_placeholder_position = (std::numeric_limits<std::size_t>::max)();
|
||||
|
||||
template<typename It>
|
||||
class dense_set_iterator final {
|
||||
template<typename>
|
||||
@@ -152,9 +154,7 @@ public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
constexpr dense_set_local_iterator() noexcept
|
||||
: it{},
|
||||
offset{} {}
|
||||
constexpr dense_set_local_iterator() noexcept = default;
|
||||
|
||||
constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept
|
||||
: it{iter},
|
||||
@@ -187,8 +187,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
It it;
|
||||
std::size_t offset;
|
||||
It it{};
|
||||
std::size_t offset{dense_set_placeholder_position};
|
||||
};
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
@@ -220,6 +220,7 @@ template<typename Type, typename Hash, typename KeyEqual, typename Allocator>
|
||||
class dense_set {
|
||||
static constexpr float default_threshold = 0.875f;
|
||||
static constexpr std::size_t minimum_capacity = 8u;
|
||||
static constexpr std::size_t placeholder_position = internal::dense_set_placeholder_position;
|
||||
|
||||
using node_type = std::pair<std::size_t, Type>;
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
@@ -233,10 +234,10 @@ class dense_set {
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) {
|
||||
for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
|
||||
if(packed.second()(*it, value)) {
|
||||
return begin() + static_cast<difference_type>(it.index());
|
||||
[[nodiscard]] auto constrained_find(const Other &value, const std::size_t bucket) {
|
||||
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) {
|
||||
if(packed.second()(packed.first()[offset].second, value)) {
|
||||
return begin() + static_cast<typename iterator::difference_type>(offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,10 +245,10 @@ class dense_set {
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const {
|
||||
for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
|
||||
if(packed.second()(*it, value)) {
|
||||
return cbegin() + static_cast<difference_type>(it.index());
|
||||
[[nodiscard]] auto constrained_find(const Other &value, const std::size_t bucket) const {
|
||||
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) {
|
||||
if(packed.second()(packed.first()[offset].second, value)) {
|
||||
return cbegin() + static_cast<typename const_iterator::difference_type>(offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,7 +625,7 @@ public:
|
||||
* @return Number of elements removed (either 0 or 1).
|
||||
*/
|
||||
size_type erase(const value_type &value) {
|
||||
for(size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].first) {
|
||||
for(size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != placeholder_position; curr = &packed.first()[*curr].first) {
|
||||
if(packed.second()(packed.first()[*curr].second, value)) {
|
||||
const auto index = *curr;
|
||||
*curr = packed.first()[*curr].first;
|
||||
@@ -787,7 +788,7 @@ public:
|
||||
* @return An iterator to the end of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
|
||||
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -805,7 +806,7 @@ public:
|
||||
* @return An iterator to the end of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
|
||||
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -882,7 +883,7 @@ public:
|
||||
sparse.first().resize(sz);
|
||||
|
||||
for(auto &&elem: sparse.first()) {
|
||||
elem = (std::numeric_limits<size_type>::max)();
|
||||
elem = placeholder_position;
|
||||
}
|
||||
|
||||
for(size_type pos{}, last = size(); pos < last; ++pos) {
|
||||
|
||||
+225
-144
@@ -6,10 +6,10 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/utility.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "type_info.hpp"
|
||||
#include "type_traits.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
@@ -17,49 +17,62 @@ namespace entt {
|
||||
namespace internal {
|
||||
|
||||
enum class any_request : std::uint8_t {
|
||||
info,
|
||||
transfer,
|
||||
assign,
|
||||
destroy,
|
||||
compare,
|
||||
copy,
|
||||
move,
|
||||
get
|
||||
move
|
||||
};
|
||||
|
||||
template<std::size_t Len, std::size_t Align>
|
||||
struct basic_any_storage {
|
||||
static constexpr bool has_buffer = true;
|
||||
union {
|
||||
const void *instance{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
alignas(Align) std::byte buffer[Len];
|
||||
};
|
||||
};
|
||||
|
||||
template<std::size_t Align>
|
||||
struct basic_any_storage<0u, Align> {
|
||||
static constexpr bool has_buffer = false;
|
||||
const void *instance{};
|
||||
};
|
||||
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
// NOLINTNEXTLINE(bugprone-sizeof-expression)
|
||||
struct in_situ: std::bool_constant<(Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>> {};
|
||||
|
||||
template<std::size_t Len, std::size_t Align>
|
||||
struct in_situ<void, Len, Align>: std::false_type {};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief A SBO friendly, type-safe container for single values of any type.
|
||||
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||
* @tparam Len Size of the buffer reserved for the small buffer optimization.
|
||||
* @tparam Align Optional alignment requirement.
|
||||
*/
|
||||
template<std::size_t Len, std::size_t Align>
|
||||
class basic_any {
|
||||
class basic_any: private internal::basic_any_storage<Len, Align> {
|
||||
using request = internal::any_request;
|
||||
using base_type = internal::basic_any_storage<Len, Align>;
|
||||
using vtable_type = const void *(const request, const basic_any &, const void *);
|
||||
|
||||
struct storage_type {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
alignas(Align) std::byte data[Len + static_cast<std::size_t>(Len == 0u)];
|
||||
};
|
||||
using deleter_type = void(const basic_any &);
|
||||
|
||||
template<typename Type>
|
||||
// NOLINTNEXTLINE(bugprone-sizeof-expression)
|
||||
static constexpr bool in_situ = (Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>;
|
||||
static constexpr bool in_situ_v = internal::in_situ<Type, Len, Align>::value;
|
||||
|
||||
template<typename Type>
|
||||
static const void *basic_vtable(const request req, const basic_any &value, const void *other) {
|
||||
static_assert(!std::is_void_v<Type> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
|
||||
const Type *elem = nullptr;
|
||||
static_assert(std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
|
||||
|
||||
if constexpr(in_situ<Type>) {
|
||||
elem = (value.mode == any_policy::embedded) ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
|
||||
} else {
|
||||
elem = static_cast<const Type *>(value.instance);
|
||||
}
|
||||
|
||||
switch(req) {
|
||||
switch(const auto *elem = static_cast<const Type *>(value.data()); req) {
|
||||
case request::info:
|
||||
return &type_id<Type>();
|
||||
case request::transfer:
|
||||
if constexpr(std::is_move_assignable_v<Type>) {
|
||||
// NOLINTNEXTLINE(bugprone-casting-through-void)
|
||||
@@ -73,15 +86,6 @@ class basic_any {
|
||||
return other;
|
||||
}
|
||||
break;
|
||||
case request::destroy:
|
||||
if constexpr(in_situ<Type>) {
|
||||
(value.mode == any_policy::embedded) ? elem->~Type() : (delete elem);
|
||||
} else if constexpr(std::is_array_v<Type>) {
|
||||
delete[] elem;
|
||||
} else {
|
||||
delete elem;
|
||||
}
|
||||
break;
|
||||
case request::compare:
|
||||
if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
|
||||
return (*elem == *static_cast<const Type *>(other)) ? other : nullptr;
|
||||
@@ -96,67 +100,86 @@ class basic_any {
|
||||
break;
|
||||
case request::move:
|
||||
ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy");
|
||||
if constexpr(in_situ<Type>) {
|
||||
if constexpr(in_situ_v<Type>) {
|
||||
// NOLINTNEXTLINE(bugprone-casting-through-void, bugprone-multi-level-implicit-pointer-conversion)
|
||||
return ::new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(elem))};
|
||||
}
|
||||
[[fallthrough]];
|
||||
case request::get:
|
||||
ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy");
|
||||
if constexpr(in_situ<Type>) {
|
||||
// NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
|
||||
return elem;
|
||||
return ::new(&static_cast<basic_any *>(const_cast<void *>(other))->buffer) Type{std::move(*const_cast<Type *>(elem))};
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
static void basic_deleter(const basic_any &value) {
|
||||
static_assert(std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
|
||||
ENTT_ASSERT((value.mode == any_policy::dynamic) || ((value.mode == any_policy::embedded) && !std::is_trivially_destructible_v<Type>), "Unexpected policy");
|
||||
|
||||
const auto *elem = static_cast<const Type *>(value.data());
|
||||
|
||||
if constexpr(in_situ_v<Type>) {
|
||||
(value.mode == any_policy::embedded) ? elem->~Type() : (delete elem);
|
||||
} else if constexpr(std::is_array_v<Type>) {
|
||||
delete[] elem;
|
||||
} else {
|
||||
delete elem;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Type, typename... Args>
|
||||
void initialize([[maybe_unused]] Args &&...args) {
|
||||
if constexpr(!std::is_void_v<Type>) {
|
||||
using plain_type = std::remove_cv_t<std::remove_reference_t<Type>>;
|
||||
using plain_type = std::remove_const_t<std::remove_reference_t<Type>>;
|
||||
|
||||
info = &type_id<plain_type>();
|
||||
vtable = basic_vtable<plain_type>;
|
||||
vtable = basic_vtable<plain_type>;
|
||||
underlying_type = type_hash<plain_type>::value();
|
||||
|
||||
if constexpr(std::is_lvalue_reference_v<Type>) {
|
||||
static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
|
||||
mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
|
||||
// NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
|
||||
instance = (std::addressof(args), ...);
|
||||
} else if constexpr(in_situ<plain_type>) {
|
||||
mode = any_policy::embedded;
|
||||
|
||||
if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
|
||||
::new(&storage) plain_type{std::forward<Args>(args)...};
|
||||
} else {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
|
||||
::new(&storage) plain_type(std::forward<Args>(args)...);
|
||||
}
|
||||
if constexpr(std::is_void_v<Type>) {
|
||||
deleter = nullptr;
|
||||
mode = any_policy::empty;
|
||||
this->instance = nullptr;
|
||||
} else if constexpr(std::is_lvalue_reference_v<Type>) {
|
||||
deleter = nullptr;
|
||||
mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
|
||||
static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
|
||||
// NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
|
||||
this->instance = (std::addressof(args), ...);
|
||||
} else if constexpr(in_situ_v<plain_type>) {
|
||||
if constexpr(std::is_trivially_destructible_v<plain_type>) {
|
||||
deleter = nullptr;
|
||||
} else {
|
||||
mode = any_policy::dynamic;
|
||||
deleter = &basic_deleter<plain_type>;
|
||||
}
|
||||
|
||||
if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
|
||||
instance = new plain_type{std::forward<Args>(args)...};
|
||||
} else if constexpr(std::is_array_v<plain_type>) {
|
||||
static_assert(sizeof...(Args) == 0u, "Invalid arguments");
|
||||
instance = new plain_type[std::extent_v<plain_type>]();
|
||||
} else {
|
||||
instance = new plain_type(std::forward<Args>(args)...);
|
||||
}
|
||||
mode = any_policy::embedded;
|
||||
|
||||
if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
|
||||
::new(&this->buffer) plain_type{std::forward<Args>(args)...};
|
||||
} else {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
|
||||
::new(&this->buffer) plain_type(std::forward<Args>(args)...);
|
||||
}
|
||||
} else {
|
||||
deleter = &basic_deleter<plain_type>;
|
||||
mode = any_policy::dynamic;
|
||||
|
||||
if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
|
||||
this->instance = new plain_type{std::forward<Args>(args)...};
|
||||
} else if constexpr(std::is_array_v<plain_type>) {
|
||||
static_assert(sizeof...(Args) == 0u, "Invalid arguments");
|
||||
this->instance = new plain_type[std::extent_v<plain_type>]();
|
||||
} else {
|
||||
this->instance = new plain_type(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
basic_any(const basic_any &other, const any_policy pol) noexcept
|
||||
: instance{other.data()},
|
||||
info{other.info},
|
||||
vtable{other.vtable},
|
||||
mode{pol} {}
|
||||
void invoke_deleter_if_exists() {
|
||||
if(deleter != nullptr) {
|
||||
deleter(*this);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Size of the internal storage. */
|
||||
/*! @brief Size of the internal buffer. */
|
||||
static constexpr auto length = Len;
|
||||
/*! @brief Alignment requirement. */
|
||||
static constexpr auto alignment = Align;
|
||||
@@ -173,7 +196,7 @@ public:
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
|
||||
: instance{} {
|
||||
: base_type{} {
|
||||
initialize<Type>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -184,11 +207,14 @@ public:
|
||||
*/
|
||||
template<typename Type>
|
||||
explicit basic_any(std::in_place_t, Type *value)
|
||||
: instance{} {
|
||||
: base_type{} {
|
||||
static_assert(!std::is_const_v<Type> && !std::is_void_v<Type>, "Non-const non-void pointer required");
|
||||
|
||||
if(value != nullptr) {
|
||||
if(value == nullptr) {
|
||||
initialize<void>();
|
||||
} else {
|
||||
initialize<Type &>(*value);
|
||||
deleter = &basic_deleter<Type>;
|
||||
mode = any_policy::dynamic;
|
||||
}
|
||||
}
|
||||
@@ -208,9 +234,7 @@ public:
|
||||
*/
|
||||
basic_any(const basic_any &other)
|
||||
: basic_any{} {
|
||||
if(other.vtable) {
|
||||
other.vtable(request::copy, other, this);
|
||||
}
|
||||
other.vtable(request::copy, other, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,22 +242,21 @@ public:
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_any(basic_any &&other) noexcept
|
||||
: instance{},
|
||||
info{other.info},
|
||||
: base_type{},
|
||||
vtable{other.vtable},
|
||||
deleter{other.deleter},
|
||||
underlying_type{other.underlying_type},
|
||||
mode{other.mode} {
|
||||
if(other.mode == any_policy::embedded) {
|
||||
other.vtable(request::move, other, this);
|
||||
} else if(other.mode != any_policy::empty) {
|
||||
instance = std::exchange(other.instance, nullptr);
|
||||
this->instance = std::exchange(other.instance, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
/*! @brief Frees the internal storage, whatever it means. */
|
||||
/*! @brief Frees the internal buffer, whatever it means. */
|
||||
~basic_any() {
|
||||
if(owner()) {
|
||||
vtable(request::destroy, *this, nullptr);
|
||||
}
|
||||
invoke_deleter_if_exists();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,10 +266,12 @@ public:
|
||||
*/
|
||||
basic_any &operator=(const basic_any &other) {
|
||||
if(this != &other) {
|
||||
reset();
|
||||
invoke_deleter_if_exists();
|
||||
|
||||
if(other.vtable) {
|
||||
if(other) {
|
||||
other.vtable(request::copy, other, this);
|
||||
} else {
|
||||
initialize<void>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,26 +280,25 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
*
|
||||
* @warning
|
||||
* Self-moving puts objects in a safe but unspecified state.
|
||||
*
|
||||
* @param other The instance to move from.
|
||||
* @return This any object.
|
||||
*/
|
||||
basic_any &operator=(basic_any &&other) noexcept {
|
||||
reset();
|
||||
if(this != &other) {
|
||||
invoke_deleter_if_exists();
|
||||
|
||||
if(other.mode == any_policy::embedded) {
|
||||
other.vtable(request::move, other, this);
|
||||
} else if(other.mode != any_policy::empty) {
|
||||
instance = std::exchange(other.instance, nullptr);
|
||||
if(other.mode == any_policy::embedded) {
|
||||
other.vtable(request::move, other, this);
|
||||
} else if(other.mode != any_policy::empty) {
|
||||
this->instance = std::exchange(other.instance, nullptr);
|
||||
}
|
||||
|
||||
vtable = other.vtable;
|
||||
deleter = other.deleter;
|
||||
underlying_type = other.underlying_type;
|
||||
mode = other.mode;
|
||||
}
|
||||
|
||||
info = other.info;
|
||||
vtable = other.vtable;
|
||||
mode = other.mode;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -291,11 +315,48 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the object type if any, `type_id<void>()` otherwise.
|
||||
* @return The object type if any, `type_id<void>()` otherwise.
|
||||
* @brief Returns false if a wrapper is empty, true otherwise.
|
||||
* @return False if the wrapper is empty, true otherwise.
|
||||
*/
|
||||
[[nodiscard]] const type_info &type() const noexcept {
|
||||
return (info == nullptr) ? type_id<void>() : *info;
|
||||
[[nodiscard]] bool has_value() const noexcept {
|
||||
return (mode != any_policy::empty);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns false if the wrapper does not contain the expected type,
|
||||
* true otherwise.
|
||||
* @param req Expected type.
|
||||
* @return False if the wrapper does not contain the expected type, true
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool has_value(const type_info &req) const noexcept {
|
||||
return (underlying_type == req.hash());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns false if the wrapper does not contain the expected type,
|
||||
* true otherwise.
|
||||
* @tparam Type Expected type.
|
||||
* @return False if the wrapper does not contain the expected type, true
|
||||
* otherwise.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] bool has_value() const noexcept {
|
||||
static_assert(std::is_same_v<std::remove_const_t<Type>, Type>, "Invalid type");
|
||||
return (underlying_type == type_hash<Type>::value());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the object type info if any, `type_id<void>()` otherwise.
|
||||
* @return The object type info if any, `type_id<void>()` otherwise.
|
||||
*/
|
||||
[[nodiscard]] const type_info &info() const noexcept {
|
||||
return *static_cast<const type_info *>(vtable(request::info, *this, nullptr));
|
||||
}
|
||||
|
||||
/*! @copydoc info */
|
||||
[[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept {
|
||||
return info();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,7 +364,11 @@ public:
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
[[nodiscard]] const void *data() const noexcept {
|
||||
return (mode == any_policy::embedded) ? vtable(request::get, *this, nullptr) : instance;
|
||||
if constexpr(base_type::has_buffer) {
|
||||
return (mode == any_policy::embedded) ? &this->buffer : this->instance;
|
||||
} else {
|
||||
return this->instance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -312,7 +377,17 @@ public:
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
[[nodiscard]] const void *data(const type_info &req) const noexcept {
|
||||
return (type() == req) ? data() : nullptr;
|
||||
return has_value(req) ? data() : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an opaque pointer to the contained instance.
|
||||
* @tparam Type Expected type.
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] const Type *data() const noexcept {
|
||||
return has_value<std::remove_const_t<Type>>() ? static_cast<const Type *>(data()) : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,7 +395,7 @@ public:
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
[[nodiscard]] void *data() noexcept {
|
||||
return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data());
|
||||
return (mode == any_policy::cref) ? nullptr : const_cast<void *>(std::as_const(*this).data());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -329,7 +404,21 @@ public:
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
[[nodiscard]] void *data(const type_info &req) noexcept {
|
||||
return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
|
||||
return (mode == any_policy::cref) ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an opaque pointer to the contained instance.
|
||||
* @tparam Type Expected type.
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] Type *data() noexcept {
|
||||
if constexpr(std::is_const_v<Type>) {
|
||||
return std::as_const(*this).template data<std::remove_const_t<Type>>();
|
||||
} else {
|
||||
return (mode == any_policy::cref) ? nullptr : const_cast<Type *>(std::as_const(*this).template data<std::remove_const_t<Type>>());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -340,7 +429,7 @@ public:
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
void emplace(Args &&...args) {
|
||||
reset();
|
||||
invoke_deleter_if_exists();
|
||||
initialize<Type>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -350,7 +439,7 @@ public:
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
bool assign(const basic_any &other) {
|
||||
if(vtable && mode != any_policy::cref && *info == other.type()) {
|
||||
if(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) {
|
||||
return (vtable(request::assign, *this, other.data()) != nullptr);
|
||||
}
|
||||
|
||||
@@ -360,12 +449,8 @@ public:
|
||||
/*! @copydoc assign */
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
|
||||
bool assign(basic_any &&other) {
|
||||
if(vtable && mode != any_policy::cref && *info == other.type()) {
|
||||
if(auto *val = other.data(); val) {
|
||||
return (vtable(request::transfer, *this, val) != nullptr);
|
||||
}
|
||||
|
||||
return (vtable(request::assign, *this, std::as_const(other).data()) != nullptr);
|
||||
if(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) {
|
||||
return (other.mode == any_policy::cref) ? (vtable(request::assign, *this, std::as_const(other).data()) != nullptr) : (vtable(request::transfer, *this, other.data()) != nullptr);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -373,14 +458,8 @@ public:
|
||||
|
||||
/*! @brief Destroys contained object */
|
||||
void reset() {
|
||||
if(owner()) {
|
||||
vtable(request::destroy, *this, nullptr);
|
||||
}
|
||||
|
||||
instance = nullptr;
|
||||
info = nullptr;
|
||||
vtable = nullptr;
|
||||
mode = any_policy::empty;
|
||||
invoke_deleter_if_exists();
|
||||
initialize<void>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -388,7 +467,7 @@ public:
|
||||
* @return False if the wrapper is empty, true otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const noexcept {
|
||||
return vtable != nullptr;
|
||||
return has_value();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -397,11 +476,11 @@ public:
|
||||
* @return False if the two objects differ in their content, true otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool operator==(const basic_any &other) const noexcept {
|
||||
if(vtable && *info == other.type()) {
|
||||
if(other && (underlying_type == other.underlying_type)) {
|
||||
return (vtable(request::compare, *this, other.data()) != nullptr);
|
||||
}
|
||||
|
||||
return (!vtable && !other.vtable);
|
||||
return (!*this && !other);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -418,12 +497,19 @@ public:
|
||||
* @return A wrapper that shares a reference to an unmanaged object.
|
||||
*/
|
||||
[[nodiscard]] basic_any as_ref() noexcept {
|
||||
return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)};
|
||||
basic_any other = std::as_const(*this).as_ref();
|
||||
other.mode = (mode == any_policy::cref ? any_policy::cref : any_policy::ref);
|
||||
return other;
|
||||
}
|
||||
|
||||
/*! @copydoc as_ref */
|
||||
[[nodiscard]] basic_any as_ref() const noexcept {
|
||||
return basic_any{*this, any_policy::cref};
|
||||
basic_any other{};
|
||||
other.instance = data();
|
||||
other.vtable = vtable;
|
||||
other.underlying_type = underlying_type;
|
||||
other.mode = any_policy::cref;
|
||||
return other;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -443,19 +529,16 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
union {
|
||||
const void *instance;
|
||||
storage_type storage;
|
||||
};
|
||||
const type_info *info{};
|
||||
vtable_type *vtable{};
|
||||
any_policy mode{any_policy::empty};
|
||||
deleter_type *deleter{};
|
||||
id_type underlying_type{};
|
||||
any_policy mode{};
|
||||
};
|
||||
|
||||
/**
|
||||
* @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 Len Size of the buffer reserved for the small buffer optimization.
|
||||
* @tparam Align Alignment requirement.
|
||||
* @param data Target any object.
|
||||
* @return The element converted to the requested type.
|
||||
@@ -480,7 +563,7 @@ template<typename Type, std::size_t Len, std::size_t Align>
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
|
||||
[[nodiscard]] std::remove_const_t<Type> any_cast(basic_any<Len, Align> &&data) noexcept {
|
||||
if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
|
||||
if constexpr(std::is_copy_constructible_v<std::remove_const_t<std::remove_reference_t<Type>>>) {
|
||||
if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
|
||||
return static_cast<Type>(std::move(*instance));
|
||||
}
|
||||
@@ -496,8 +579,7 @@ template<typename Type, std::size_t Len, std::size_t Align>
|
||||
/*! @copydoc any_cast */
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
[[nodiscard]] const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
|
||||
const auto &info = type_id<std::remove_cv_t<Type>>();
|
||||
return static_cast<const Type *>(data->data(info));
|
||||
return data->template data<std::remove_const_t<Type>>();
|
||||
}
|
||||
|
||||
/*! @copydoc any_cast */
|
||||
@@ -507,15 +589,14 @@ template<typename Type, std::size_t Len, std::size_t Align>
|
||||
// last attempt to make wrappers for const references return their values
|
||||
return any_cast<Type>(&std::as_const(*data));
|
||||
} else {
|
||||
const auto &info = type_id<std::remove_cv_t<Type>>();
|
||||
return static_cast<Type *>(data->data(info));
|
||||
return data->template data<Type>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs a wrapper from a given type, passing it all arguments.
|
||||
* @tparam Type Type of object to use to initialize the wrapper.
|
||||
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||
* @tparam Len Size of the buffer reserved for the small buffer optimization.
|
||||
* @tparam Align Optional alignment requirement.
|
||||
* @tparam Args Types of arguments to use to construct the new instance.
|
||||
* @param args Parameters to use to construct the instance.
|
||||
@@ -528,7 +609,7 @@ template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align
|
||||
|
||||
/**
|
||||
* @brief Forwards its argument and avoids copies for lvalue references.
|
||||
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||
* @tparam Len Size of the buffer reserved for the small buffer optimization.
|
||||
* @tparam Align Optional alignment requirement.
|
||||
* @tparam Type Type of argument to use to construct the new instance.
|
||||
* @param value Parameter to use to construct the instance.
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
#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
|
||||
@@ -22,7 +22,7 @@ struct compressed_pair_element {
|
||||
// NOLINTNEXTLINE(modernize-use-equals-default)
|
||||
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<Type>) {}
|
||||
|
||||
template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Arg>>, compressed_pair_element>>>
|
||||
template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_reference_t<Arg>>, compressed_pair_element>>>
|
||||
constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<Type, Arg>)
|
||||
: value{std::forward<Arg>(arg)} {}
|
||||
|
||||
@@ -52,7 +52,7 @@ struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Ty
|
||||
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<base_type>)
|
||||
: base_type{} {}
|
||||
|
||||
template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Arg>>, compressed_pair_element>>>
|
||||
template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_reference_t<Arg>>, compressed_pair_element>>>
|
||||
constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<base_type, Arg>)
|
||||
: base_type{std::forward<Arg>(arg)} {}
|
||||
|
||||
|
||||
@@ -15,8 +15,10 @@ namespace entt {
|
||||
*/
|
||||
template<typename...>
|
||||
class family {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
inline static ENTT_MAYBE_ATOMIC(id_type) identifier{};
|
||||
static auto identifier() noexcept {
|
||||
static ENTT_MAYBE_ATOMIC(id_type) value{};
|
||||
return value++;
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
@@ -25,7 +27,7 @@ public:
|
||||
/*! @brief Statically generated unique identifier for the given type. */
|
||||
template<typename... Type>
|
||||
// at the time I'm writing, clang crashes during compilation if auto is used instead of family_type
|
||||
inline static const value_type value = identifier++;
|
||||
inline static const value_type value = identifier();
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -9,15 +9,15 @@ namespace entt {
|
||||
|
||||
/*! @brief Possible modes of an any object. */
|
||||
enum class any_policy : std::uint8_t {
|
||||
/*! @brief Default mode, the object does not own any elements. */
|
||||
/*! @brief Default mode, no element available. */
|
||||
empty,
|
||||
/*! @brief Owning mode, the object owns a dynamically allocated element. */
|
||||
/*! @brief Owning mode, dynamically allocated element. */
|
||||
dynamic,
|
||||
/*! @brief Owning mode, the object owns an embedded element. */
|
||||
/*! @brief Owning mode, embedded element. */
|
||||
embedded,
|
||||
/*! @brief Aliasing mode, the object _points_ to a non-const element. */
|
||||
/*! @brief Aliasing mode, non-const reference. */
|
||||
ref,
|
||||
/*! @brief Const aliasing mode, the object _points_ to a const element. */
|
||||
/*! @brief Const aliasing mode, const reference. */
|
||||
cref
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
@@ -32,9 +31,9 @@ struct basic_hashed_string {
|
||||
using size_type = std::size_t;
|
||||
using hash_type = id_type;
|
||||
|
||||
const value_type *repr;
|
||||
size_type length;
|
||||
hash_type hash;
|
||||
const value_type *repr{};
|
||||
hash_type hash{fnv_1a_params<>::offset};
|
||||
size_type length{};
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
@@ -62,23 +61,12 @@ class basic_hashed_string: internal::basic_hashed_string<Char> {
|
||||
|
||||
struct const_wrapper {
|
||||
// non-explicit constructor on purpose
|
||||
constexpr const_wrapper(const Char *str) noexcept
|
||||
constexpr const_wrapper(const typename base_type::value_type *str) noexcept
|
||||
: repr{str} {}
|
||||
|
||||
const Char *repr;
|
||||
const typename base_type::value_type *repr;
|
||||
};
|
||||
|
||||
// Fowler–Noll–Vo hash function v. 1a - the good
|
||||
[[nodiscard]] static constexpr auto helper(const std::basic_string_view<Char> view) noexcept {
|
||||
base_type base{view.data(), view.size(), params::offset};
|
||||
|
||||
for(auto &&curr: view) {
|
||||
base.hash = (base.hash ^ static_cast<id_type>(curr)) * params::prime;
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Character type. */
|
||||
using value_type = typename base_type::value_type;
|
||||
@@ -128,7 +116,14 @@ public:
|
||||
* @param len Length of the string to hash.
|
||||
*/
|
||||
constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept
|
||||
: base_type{helper({str, len})} {}
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
|
||||
: base_type{str} {
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
for(; base_type::length < len; ++base_type::length) {
|
||||
base_type::hash = (base_type::hash ^ static_cast<id_type>(str[base_type::length])) * params::prime;
|
||||
}
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs a hashed string from an array of const characters.
|
||||
@@ -138,7 +133,12 @@ public:
|
||||
template<std::size_t N>
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
ENTT_CONSTEVAL basic_hashed_string(const value_type (&str)[N]) noexcept
|
||||
: base_type{helper({static_cast<const value_type *>(str)})} {}
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
|
||||
: base_type{str} {
|
||||
for(; str[base_type::length]; ++base_type::length) {
|
||||
base_type::hash = (base_type::hash ^ static_cast<id_type>(str[base_type::length])) * params::prime;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Explicit constructor on purpose to avoid constructing a hashed
|
||||
@@ -150,10 +150,16 @@ public:
|
||||
* @param wrapper Helps achieving the purpose by relying on overloading.
|
||||
*/
|
||||
explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept
|
||||
: base_type{helper({wrapper.repr})} {}
|
||||
: base_type{wrapper.repr} {
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
for(; wrapper.repr[base_type::length]; ++base_type::length) {
|
||||
base_type::hash = (base_type::hash ^ static_cast<id_type>(wrapper.repr[base_type::length])) * params::prime;
|
||||
}
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the size a hashed string.
|
||||
* @brief Returns the size of a hashed string.
|
||||
* @return The size of the hashed string.
|
||||
*/
|
||||
[[nodiscard]] constexpr size_type size() const noexcept {
|
||||
@@ -177,7 +183,7 @@ public:
|
||||
}
|
||||
|
||||
/*! @copydoc data */
|
||||
[[nodiscard]] constexpr operator const value_type *() const noexcept {
|
||||
[[nodiscard]] explicit constexpr operator const value_type *() const noexcept {
|
||||
return data();
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ struct forward_apply: private Func {
|
||||
* @tparam Func Type of underlying invocable object.
|
||||
*/
|
||||
template<typename Func>
|
||||
forward_apply(Func) -> forward_apply<std::remove_reference_t<std::remove_cv_t<Func>>>;
|
||||
forward_apply(Func) -> forward_apply<std::remove_reference_t<std::remove_const_t<Func>>>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
+19
-11
@@ -5,7 +5,6 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/attribute.h"
|
||||
#include "fwd.hpp"
|
||||
#include "hashed_string.hpp"
|
||||
|
||||
@@ -21,15 +20,24 @@ struct ENTT_API type_index final {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr const char *pretty_function() noexcept {
|
||||
#if defined ENTT_PRETTY_FUNCTION
|
||||
return static_cast<const char *>(ENTT_PRETTY_FUNCTION);
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr auto stripped_type_name() noexcept {
|
||||
#if defined ENTT_PRETTY_FUNCTION
|
||||
const std::string_view pretty_function{static_cast<const char *>(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);
|
||||
const std::string_view full_name{pretty_function<Type>()};
|
||||
auto first = full_name.find_first_not_of(' ', full_name.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
|
||||
auto value = full_name.substr(first, full_name.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
|
||||
return value;
|
||||
#else
|
||||
return std::string_view{""};
|
||||
return std::string_view{};
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -138,9 +146,9 @@ struct type_info final {
|
||||
template<typename Type>
|
||||
// NOLINTBEGIN(modernize-use-transparent-functors)
|
||||
constexpr type_info(std::in_place_type_t<Type>) noexcept
|
||||
: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
|
||||
identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
|
||||
alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {}
|
||||
: seq{type_index<std::remove_const_t<std::remove_reference_t<Type>>>::value()},
|
||||
identifier{type_hash<std::remove_const_t<std::remove_reference_t<Type>>>::value()},
|
||||
alias{type_name<std::remove_const_t<std::remove_reference_t<Type>>>::value()} {}
|
||||
// NOLINTEND(modernize-use-transparent-functors)
|
||||
|
||||
/**
|
||||
@@ -249,11 +257,11 @@ private:
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] const type_info &type_id() noexcept {
|
||||
if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) {
|
||||
if constexpr(std::is_same_v<Type, std::remove_const_t<std::remove_reference_t<Type>>>) {
|
||||
static const type_info instance{std::in_place_type<Type>};
|
||||
return instance;
|
||||
} else {
|
||||
return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
|
||||
return type_id<std::remove_const_t<std::remove_reference_t<Type>>>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,7 +269,7 @@ template<typename Type>
|
||||
template<typename Type>
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
|
||||
[[nodiscard]] const type_info &type_id(Type &&) noexcept {
|
||||
return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
|
||||
return type_id<std::remove_const_t<std::remove_reference_t<Type>>>();
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -691,7 +691,7 @@ struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Typ
|
||||
|
||||
/*! @copydoc is_iterator */
|
||||
template<typename Type>
|
||||
struct is_iterator<Type, std::enable_if_t<!std::is_void_v<std::remove_cv_t<std::remove_pointer_t<Type>>>>>
|
||||
struct is_iterator<Type, std::enable_if_t<!std::is_void_v<std::remove_const_t<std::remove_pointer_t<Type>>>>>
|
||||
: internal::has_iterator_category<Type> {};
|
||||
|
||||
/**
|
||||
@@ -774,7 +774,7 @@ template<typename Type>
|
||||
// NOLINTBEGIN(modernize-use-transparent-functors)
|
||||
if constexpr(std::is_array_v<Type>) {
|
||||
return false;
|
||||
} else if constexpr(is_complete_v<std::tuple_size<std::remove_cv_t<Type>>>) {
|
||||
} else if constexpr(is_complete_v<std::tuple_size<std::remove_const_t<Type>>>) {
|
||||
if constexpr(has_tuple_size_value<Type>::value) {
|
||||
return maybe_equality_comparable<Type>(0) && unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
|
||||
} else {
|
||||
|
||||
@@ -151,7 +151,7 @@ public:
|
||||
|
||||
[[nodiscard]] bool owned(const id_type hash) const noexcept override {
|
||||
for(size_type pos{}; pos < Owned; ++pos) {
|
||||
if(pools[pos]->type().hash() == hash) {
|
||||
if(pools[pos]->info().hash() == hash) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,15 +150,15 @@ public:
|
||||
destruction{allocator},
|
||||
update{allocator} {
|
||||
if constexpr(internal::has_on_construct<typename underlying_type::element_type, Registry>::value) {
|
||||
entt::sink{construction}.template connect<&underlying_type::element_type::on_construct>();
|
||||
sink{construction}.template connect<&underlying_type::element_type::on_construct>();
|
||||
}
|
||||
|
||||
if constexpr(internal::has_on_update<typename underlying_type::element_type, Registry>::value) {
|
||||
entt::sink{update}.template connect<&underlying_type::element_type::on_update>();
|
||||
sink{update}.template connect<&underlying_type::element_type::on_update>();
|
||||
}
|
||||
|
||||
if constexpr(internal::has_on_destroy<typename underlying_type::element_type, Registry>::value) {
|
||||
entt::sink{destruction}.template connect<&underlying_type::element_type::on_destroy>();
|
||||
sink{destruction}.template connect<&underlying_type::element_type::on_destroy>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,28 +169,24 @@ public:
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-use-after-move)
|
||||
basic_sigh_mixin(basic_sigh_mixin &&other) noexcept
|
||||
: underlying_type{std::move(other)},
|
||||
: underlying_type{static_cast<underlying_type &&>(other)},
|
||||
owner{other.owner},
|
||||
construction{std::move(other.construction)},
|
||||
destruction{std::move(other.destruction)},
|
||||
update{std::move(other.update)} {}
|
||||
// NOLINTEND(bugprone-use-after-move)
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-use-after-move)
|
||||
basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator)
|
||||
: underlying_type{std::move(other), allocator},
|
||||
: underlying_type{static_cast<underlying_type &&>(other), allocator},
|
||||
owner{other.owner},
|
||||
construction{std::move(other.construction), allocator},
|
||||
destruction{std::move(other.destruction), allocator},
|
||||
update{std::move(other.update), allocator} {}
|
||||
// NOLINTEND(bugprone-use-after-move)
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_sigh_mixin() override = default;
|
||||
@@ -454,26 +450,22 @@ public:
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-use-after-move)
|
||||
basic_reactive_mixin(basic_reactive_mixin &&other) noexcept
|
||||
: underlying_type{std::move(other)},
|
||||
: underlying_type{static_cast<underlying_type &&>(other)},
|
||||
owner{other.owner},
|
||||
conn{} {
|
||||
conn{std::move(other.conn)} {
|
||||
}
|
||||
// NOLINTEND(bugprone-use-after-move)
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-use-after-move)
|
||||
basic_reactive_mixin(basic_reactive_mixin &&other, const allocator_type &allocator)
|
||||
: underlying_type{std::move(other), allocator},
|
||||
: underlying_type{static_cast<underlying_type &&>(other), allocator},
|
||||
owner{other.owner},
|
||||
conn{allocator} {
|
||||
conn{std::move(other.conn), allocator} {
|
||||
}
|
||||
// NOLINTEND(bugprone-use-after-move)
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_reactive_mixin() override = default;
|
||||
|
||||
@@ -79,27 +79,28 @@ template<typename... Owned, typename... Get, typename... Exclude, typename... Ov
|
||||
struct unpack_type<const basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>>
|
||||
: unpack_type<basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {};
|
||||
|
||||
template<typename, typename>
|
||||
template<typename, typename, typename>
|
||||
struct resource_traits;
|
||||
|
||||
template<typename... Args, typename... Req>
|
||||
struct resource_traits<type_list<Args...>, type_list<Req...>> {
|
||||
template<typename Registry, typename... Args, typename... Req>
|
||||
struct resource_traits<Registry, 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...>;
|
||||
static constexpr auto sync_point = (std::is_same_v<Args, Registry> || ...);
|
||||
};
|
||||
|
||||
template<typename... Req, typename Ret, typename... Args>
|
||||
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource_traits(Ret (*)(Args...));
|
||||
template<typename Registry, typename... Req, typename Ret, typename... Args>
|
||||
resource_traits<Registry, type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource_traits(Ret (*)(Args...));
|
||||
|
||||
template<typename... Req, typename Ret, typename Type, typename... Args>
|
||||
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (*)(Type &, Args...));
|
||||
template<typename Registry, typename... Req, typename Ret, typename Type, typename... Args>
|
||||
resource_traits<Registry, type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (*)(Type &, Args...));
|
||||
|
||||
template<typename... Req, typename Ret, typename Class, typename... Args>
|
||||
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...));
|
||||
template<typename Registry, typename... Req, typename Ret, typename Class, typename... Args>
|
||||
resource_traits<Registry, type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...));
|
||||
|
||||
template<typename... Req, typename Ret, typename Class, typename... Args>
|
||||
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const);
|
||||
template<typename Registry, typename... Req, typename Ret, typename Class, typename... Args>
|
||||
resource_traits<Registry, type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const);
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
@@ -169,9 +170,9 @@ class basic_organizer final {
|
||||
}
|
||||
|
||||
template<typename... RO, typename... RW>
|
||||
void track_dependencies(std::size_t index, const bool requires_registry, type_list<RO...>, type_list<RW...>) {
|
||||
void track_dependencies(std::size_t index, const bool sync_point, type_list<RO...>, type_list<RW...>) {
|
||||
builder.bind(static_cast<id_type>(index));
|
||||
builder.set(type_hash<Registry>::value(), requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u));
|
||||
builder.set(type_hash<Registry>::value(), sync_point || (sizeof...(RO) + sizeof...(RW) == 0u));
|
||||
(builder.ro(type_hash<RO>::value()), ...);
|
||||
(builder.rw(type_hash<RW>::value()), ...);
|
||||
}
|
||||
@@ -316,8 +317,7 @@ public:
|
||||
*/
|
||||
template<auto Candidate, typename... Req>
|
||||
void emplace(const char *name = nullptr) {
|
||||
using resource_type = decltype(internal::free_function_to_resource_traits<Req...>(Candidate));
|
||||
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, registry_type>;
|
||||
using resource_type = decltype(internal::free_function_to_resource_traits<registry_type, Req...>(Candidate));
|
||||
|
||||
callback_type *callback = +[](const void *, registry_type ®) {
|
||||
std::apply(Candidate, to_args(reg, typename resource_type::args{}));
|
||||
@@ -333,7 +333,7 @@ public:
|
||||
+[](registry_type ®) { void(to_args(reg, typename resource_type::args{})); },
|
||||
&type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
|
||||
|
||||
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
track_dependencies(vertices.size(), resource_type::sync_point, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
vertices.push_back(std::move(vdata));
|
||||
}
|
||||
|
||||
@@ -348,8 +348,7 @@ public:
|
||||
*/
|
||||
template<auto Candidate, typename... Req, typename Type>
|
||||
void emplace(Type &value_or_instance, const char *name = nullptr) {
|
||||
using resource_type = decltype(internal::constrained_function_to_resource_traits<Req...>(Candidate));
|
||||
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, registry_type>;
|
||||
using resource_type = decltype(internal::constrained_function_to_resource_traits<registry_type, Req...>(Candidate));
|
||||
|
||||
callback_type *callback = +[](const void *payload, registry_type ®) {
|
||||
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
|
||||
@@ -366,7 +365,7 @@ public:
|
||||
+[](registry_type ®) { void(to_args(reg, typename resource_type::args{})); },
|
||||
&type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
|
||||
|
||||
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
track_dependencies(vertices.size(), resource_type::sync_point, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
vertices.push_back(std::move(vdata));
|
||||
}
|
||||
|
||||
@@ -380,7 +379,7 @@ public:
|
||||
*/
|
||||
template<typename... Req>
|
||||
void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) {
|
||||
using resource_type = internal::resource_traits<type_list<>, type_list<Req...>>;
|
||||
using resource_type = internal::resource_traits<registry_type, type_list<>, type_list<Req...>>;
|
||||
track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
|
||||
vertex_data vdata{
|
||||
@@ -400,7 +399,7 @@ public:
|
||||
* @brief Generates a task graph for the current content.
|
||||
* @return The adjacency list of the task graph.
|
||||
*/
|
||||
[[nodiscard]] std::vector<vertex> graph() {
|
||||
[[nodiscard]] std::vector<vertex> graph() const {
|
||||
std::vector<vertex> adjacency_list{};
|
||||
adjacency_list.reserve(vertices.size());
|
||||
auto adjacency_matrix = builder.graph();
|
||||
|
||||
@@ -164,6 +164,10 @@ public:
|
||||
explicit registry_context(const allocator_type &allocator)
|
||||
: ctx{allocator} {}
|
||||
|
||||
void clear() noexcept {
|
||||
ctx.clear();
|
||||
}
|
||||
|
||||
template<typename Type, typename... Args>
|
||||
Type &emplace_as(const id_type id, Args &&...args) {
|
||||
return any_cast<Type &>(ctx.try_emplace(id, std::in_place_type<Type>, std::forward<Args>(args)...).first->second);
|
||||
@@ -176,7 +180,7 @@ public:
|
||||
|
||||
template<typename Type>
|
||||
Type &insert_or_assign(const id_type id, Type &&value) {
|
||||
return any_cast<std::remove_cv_t<std::remove_reference_t<Type>> &>(ctx.insert_or_assign(id, std::forward<Type>(value)).first->second);
|
||||
return any_cast<std::remove_const_t<std::remove_reference_t<Type>> &>(ctx.insert_or_assign(id, std::forward<Type>(value)).first->second);
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
@@ -187,7 +191,7 @@ public:
|
||||
template<typename Type>
|
||||
bool erase(const id_type id = type_id<Type>().hash()) {
|
||||
const auto it = ctx.find(id);
|
||||
return it != ctx.end() && it->second.type() == type_id<Type>() ? (ctx.erase(it), true) : false;
|
||||
return it != ctx.end() && it->second.info() == type_id<Type>() ? (ctx.erase(it), true) : false;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
@@ -215,7 +219,7 @@ public:
|
||||
template<typename Type>
|
||||
[[nodiscard]] bool contains(const id_type id = type_id<Type>().hash()) const {
|
||||
const auto it = ctx.find(id);
|
||||
return it != ctx.cend() && it->second.type() == type_id<Type>();
|
||||
return it != ctx.cend() && it->second.info() == type_id<Type>();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -251,7 +255,7 @@ class basic_registry {
|
||||
using storage_type = storage_for_type<Type>;
|
||||
|
||||
if(auto it = pools.find(id); it != pools.cend()) {
|
||||
ENTT_ASSERT(it->second->type() == type_id<Type>(), "Unexpected type");
|
||||
ENTT_ASSERT(it->second->info() == type_id<Type>(), "Unexpected type");
|
||||
return static_cast<storage_type &>(*it->second);
|
||||
}
|
||||
|
||||
@@ -281,7 +285,7 @@ class basic_registry {
|
||||
return &entities;
|
||||
} else {
|
||||
if(const auto it = pools.find(id); it != pools.cend()) {
|
||||
ENTT_ASSERT(it->second->type() == type_id<Type>(), "Unexpected type");
|
||||
ENTT_ASSERT(it->second->info() == type_id<Type>(), "Unexpected type");
|
||||
return static_cast<const storage_for_type<Type> *>(it->second.get());
|
||||
}
|
||||
|
||||
@@ -606,6 +610,22 @@ public:
|
||||
return assure<Type>().emplace(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns each entity in a range the given element.
|
||||
*
|
||||
* @sa emplace
|
||||
*
|
||||
* @tparam Type Type of element to create.
|
||||
* @tparam It Type of input iterator.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
*/
|
||||
template<typename Type, typename It>
|
||||
void insert(It first, It last) {
|
||||
ENTT_ASSERT(std::all_of(first, last, [this](const auto entt) { return valid(entt); }), "Invalid entity");
|
||||
assure<Type>().insert(std::move(first), std::move(last));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns each entity in a range the given element.
|
||||
*
|
||||
@@ -618,7 +638,7 @@ public:
|
||||
* @param value An instance of the element to assign.
|
||||
*/
|
||||
template<typename Type, typename It>
|
||||
void insert(It first, It last, const Type &value = {}) {
|
||||
void insert(It first, It last, const Type &value) {
|
||||
ENTT_ASSERT(std::all_of(first, last, [this](const auto entt) { return valid(entt); }), "Invalid entity");
|
||||
assure<Type>().insert(std::move(first), std::move(last), value);
|
||||
}
|
||||
|
||||
@@ -38,8 +38,7 @@ public:
|
||||
it{},
|
||||
tombstone_check{} {}
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
|
||||
runtime_view_iterator(const std::vector<Set *> &cpools, const std::vector<Set *> &ignore, iterator_type curr) noexcept
|
||||
runtime_view_iterator(const std::vector<Set *> &cpools, iterator_type curr, const std::vector<Set *> &ignore) noexcept
|
||||
: pools{&cpools},
|
||||
filter{&ignore},
|
||||
it{curr},
|
||||
@@ -126,6 +125,12 @@ class basic_runtime_view {
|
||||
static_assert(std::is_same_v<typename alloc_traits::value_type, Type *>, "Invalid value type");
|
||||
using container_type = std::vector<Type *, Allocator>;
|
||||
|
||||
[[nodiscard]] auto offset() const noexcept {
|
||||
ENTT_ASSERT(!pools.empty(), "Invalid view");
|
||||
const auto &leading = *pools.front();
|
||||
return (leading.policy() == deletion_policy::swap_only) ? leading.free_list() : leading.size();
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
@@ -133,6 +138,8 @@ public:
|
||||
using entity_type = typename Type::entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Signed integer type. */
|
||||
using difference_type = std::ptrdiff_t;
|
||||
/*! @brief Common type among all storage types. */
|
||||
using common_type = Type;
|
||||
/*! @brief Bidirectional iterator type. */
|
||||
@@ -219,10 +226,10 @@ public:
|
||||
* @return This runtime view.
|
||||
*/
|
||||
basic_runtime_view &iterate(common_type &base) {
|
||||
if(pools.empty() || !(base.size() < pools[0u]->size())) {
|
||||
if(pools.empty() || !(base.size() < pools.front()->size())) {
|
||||
pools.push_back(&base);
|
||||
} else {
|
||||
pools.push_back(std::exchange(pools[0u], &base));
|
||||
pools.push_back(std::exchange(pools.front(), &base));
|
||||
}
|
||||
|
||||
return *this;
|
||||
@@ -243,7 +250,7 @@ public:
|
||||
* @return Estimated number of entities iterated by the view.
|
||||
*/
|
||||
[[nodiscard]] size_type size_hint() const {
|
||||
return pools.empty() ? size_type{} : pools.front()->size();
|
||||
return pools.empty() ? size_type{} : offset();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,7 +262,7 @@ public:
|
||||
* @return An iterator to the first entity that has the given elements.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const {
|
||||
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->begin()};
|
||||
return pools.empty() ? iterator{} : iterator{pools, pools.front()->end() - static_cast<difference_type>(offset()), filter};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,7 +272,7 @@ public:
|
||||
* given elements.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const {
|
||||
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()};
|
||||
return pools.empty() ? iterator{} : iterator{pools, pools.front()->end(), filter};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,7 +291,8 @@ public:
|
||||
[[nodiscard]] bool contains(const entity_type entt) const {
|
||||
return !pools.empty()
|
||||
&& std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); })
|
||||
&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); });
|
||||
&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); })
|
||||
&& pools.front()->index(entt) < offset();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -375,7 +375,7 @@ protected:
|
||||
break;
|
||||
}
|
||||
|
||||
return --(end() - static_cast<difference_type>(pos));
|
||||
return iterator{packed, static_cast<difference_type>(++pos)};
|
||||
}
|
||||
|
||||
/*! @brief Forwards variables to derived classes, if any. */
|
||||
@@ -433,7 +433,7 @@ public:
|
||||
explicit basic_sparse_set(const type_info &elem, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {})
|
||||
: sparse{allocator},
|
||||
packed{allocator},
|
||||
info{&elem},
|
||||
descriptor{&elem},
|
||||
mode{pol},
|
||||
head{policy_to_head()} {
|
||||
ENTT_ASSERT(traits_type::version_mask || mode != deletion_policy::in_place, "Policy does not support zero-sized versions");
|
||||
@@ -449,7 +449,7 @@ public:
|
||||
basic_sparse_set(basic_sparse_set &&other) noexcept
|
||||
: sparse{std::move(other.sparse)},
|
||||
packed{std::move(other.packed)},
|
||||
info{other.info},
|
||||
descriptor{other.descriptor},
|
||||
mode{other.mode},
|
||||
head{std::exchange(other.head, policy_to_head())} {}
|
||||
|
||||
@@ -461,7 +461,7 @@ public:
|
||||
basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator)
|
||||
: sparse{std::move(other.sparse), allocator},
|
||||
packed{std::move(other.packed), allocator},
|
||||
info{other.info},
|
||||
descriptor{other.descriptor},
|
||||
mode{other.mode},
|
||||
head{std::exchange(other.head, policy_to_head())} {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed");
|
||||
@@ -497,7 +497,7 @@ public:
|
||||
using std::swap;
|
||||
swap(sparse, other.sparse);
|
||||
swap(packed, other.packed);
|
||||
swap(info, other.info);
|
||||
swap(descriptor, other.descriptor);
|
||||
swap(mode, other.mode);
|
||||
swap(head, other.head);
|
||||
}
|
||||
@@ -1079,11 +1079,16 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returned value type, if any.
|
||||
* @return Returned value type, if any.
|
||||
* @brief Returns a type info object for the value type, if any.
|
||||
* @return A type info object for the value type, if any.
|
||||
*/
|
||||
[[nodiscard]] const type_info &type() const noexcept {
|
||||
return *info;
|
||||
[[nodiscard]] const type_info &info() const noexcept {
|
||||
return *descriptor;
|
||||
}
|
||||
|
||||
/*! @copydoc info */
|
||||
[[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept {
|
||||
return info();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1099,7 +1104,7 @@ public:
|
||||
private:
|
||||
sparse_container_type sparse;
|
||||
packed_container_type packed;
|
||||
const type_info *info;
|
||||
const type_info *descriptor;
|
||||
deletion_policy mode;
|
||||
size_type head;
|
||||
};
|
||||
|
||||
+19
-52
@@ -450,24 +450,20 @@ public:
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-use-after-move)
|
||||
basic_storage(basic_storage &&other) noexcept
|
||||
: base_type{std::move(other)},
|
||||
: base_type{static_cast<base_type &&>(other)},
|
||||
payload{std::move(other.payload)} {}
|
||||
// NOLINTEND(bugprone-use-after-move)
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-use-after-move)
|
||||
basic_storage(basic_storage &&other, const allocator_type &allocator)
|
||||
: base_type{std::move(other), allocator},
|
||||
: base_type{static_cast<base_type &&>(other), allocator},
|
||||
payload{std::move(other.payload), allocator} {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a storage is not allowed");
|
||||
}
|
||||
// NOLINTEND(bugprone-use-after-move)
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
// NOLINTNEXTLINE(bugprone-exception-escape)
|
||||
@@ -909,12 +905,9 @@ public:
|
||||
* 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 identifier.
|
||||
*/
|
||||
template<typename... Args>
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
|
||||
void emplace(const entity_type entt, Args &&...) {
|
||||
void emplace(const entity_type entt) {
|
||||
base_type::try_emplace(entt, false);
|
||||
}
|
||||
|
||||
@@ -933,13 +926,11 @@ public:
|
||||
/**
|
||||
* @brief Assigns entities to a storage.
|
||||
* @tparam It Type of input iterator.
|
||||
* @tparam Args Types of optional arguments.
|
||||
* @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, typename... Args>
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
|
||||
void insert(It first, It last, Args &&...) {
|
||||
template<typename It>
|
||||
void insert(It first, It last) {
|
||||
for(; first != last; ++first) {
|
||||
base_type::try_emplace(*first, true);
|
||||
}
|
||||
@@ -1069,22 +1060,20 @@ public:
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-use-after-move)
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
|
||||
basic_storage(basic_storage &&other) noexcept
|
||||
: base_type{std::move(other)},
|
||||
: base_type{static_cast<base_type &&>(other)},
|
||||
placeholder{other.placeholder} {}
|
||||
// NOLINTEND(bugprone-use-after-move)
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-use-after-move)
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
|
||||
basic_storage(basic_storage &&other, const allocator_type &allocator)
|
||||
: base_type{std::move(other), allocator},
|
||||
: base_type{static_cast<base_type &&>(other), allocator},
|
||||
placeholder{other.placeholder} {}
|
||||
// NOLINTEND(bugprone-use-after-move)
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_storage() override = default;
|
||||
@@ -1106,6 +1095,16 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given storage.
|
||||
* @param other Storage to exchange the content with.
|
||||
*/
|
||||
void swap(basic_storage &other) noexcept {
|
||||
using std::swap;
|
||||
swap(placeholder, other.placeholder);
|
||||
base_type::swap(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the object assigned to an entity, that is `void`.
|
||||
*
|
||||
@@ -1175,27 +1174,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a new identifier or recycles a destroyed one.
|
||||
* @return A valid identifier.
|
||||
*/
|
||||
[[deprecated("use ::generate() instead")]] entity_type emplace() {
|
||||
return generate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a new identifier or recycles a destroyed one.
|
||||
*
|
||||
* If the requested identifier isn't in use, the suggested one is used.
|
||||
* Otherwise, a new identifier is returned.
|
||||
*
|
||||
* @param hint Required identifier.
|
||||
* @return A valid identifier.
|
||||
*/
|
||||
[[deprecated("use ::generate(hint) instead")]] entity_type emplace(const entity_type hint) {
|
||||
return generate(hint);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates a given identifier.
|
||||
* @tparam Func Types of the function objects to invoke.
|
||||
@@ -1208,17 +1186,6 @@ public:
|
||||
(std::forward<Func>(func)(), ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns each element in a range an identifier.
|
||||
* @tparam It Type of mutable forward iterator.
|
||||
* @param first An iterator to the first element of the range to generate.
|
||||
* @param last An iterator past the last element of the range to generate.
|
||||
*/
|
||||
template<typename It>
|
||||
[[deprecated("use ::generate(first, last) instead")]] void insert(It first, It last) {
|
||||
generate(std::move(first), std::move(last));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to use to _visit_ a storage.
|
||||
*
|
||||
|
||||
@@ -10,7 +10,6 @@ namespace entt {}
|
||||
#include "container/table.hpp"
|
||||
#include "core/algorithm.hpp"
|
||||
#include "core/any.hpp"
|
||||
#include "core/attribute.h"
|
||||
#include "core/bit.hpp"
|
||||
#include "core/compressed_pair.hpp"
|
||||
#include "core/enum.hpp"
|
||||
|
||||
+41
-54
@@ -4,6 +4,7 @@
|
||||
#define ENTT_META_CONTAINER_HPP
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <deque>
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
@@ -15,7 +16,9 @@
|
||||
#include <vector>
|
||||
#include "../container/dense_map.hpp"
|
||||
#include "../container/dense_set.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "context.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "meta.hpp"
|
||||
#include "type_traits.hpp"
|
||||
|
||||
@@ -24,14 +27,14 @@ namespace entt {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename, typename = void>
|
||||
struct fixed_size_sequence_container: std::true_type {};
|
||||
template<typename Type, typename = void>
|
||||
struct sequence_container_extent: integral_constant<meta_dynamic_extent> {};
|
||||
|
||||
template<typename Type>
|
||||
struct fixed_size_sequence_container<Type, std::void_t<decltype(&Type::clear)>>: std::false_type {};
|
||||
struct sequence_container_extent<Type, std::enable_if_t<is_complete_v<std::tuple_size<Type>>>>: integral_constant<std::tuple_size_v<Type>> {};
|
||||
|
||||
template<typename Type>
|
||||
inline constexpr bool fixed_size_sequence_container_v = fixed_size_sequence_container<Type>::value;
|
||||
inline constexpr std::size_t sequence_container_extent_v = sequence_container_extent<Type>::value;
|
||||
|
||||
template<typename, typename = void>
|
||||
struct key_only_associative_container: std::true_type {};
|
||||
@@ -60,15 +63,17 @@ inline constexpr bool reserve_aware_container_v = reserve_aware_container<Type>:
|
||||
*/
|
||||
template<typename Type>
|
||||
struct basic_meta_sequence_container_traits {
|
||||
static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Unexpected type");
|
||||
static_assert(std::is_same_v<Type, std::remove_const_t<std::remove_reference_t<Type>>>, "Unexpected type");
|
||||
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename meta_sequence_container::size_type;
|
||||
/*! @brief Meta iterator type. */
|
||||
using iterator = typename meta_sequence_container::iterator;
|
||||
|
||||
/*! @brief True in case of key-only containers, false otherwise. */
|
||||
static constexpr bool fixed_size = internal::fixed_size_sequence_container_v<Type>;
|
||||
/*! @brief Number of elements, or `meta_dynamic_extent` if dynamic. */
|
||||
static constexpr std::size_t extent = internal::sequence_container_extent_v<Type>;
|
||||
/*! @brief True in case of fixed size containers, false otherwise. */
|
||||
[[deprecated("use ::extent instead")]] static constexpr bool fixed_size = (extent != meta_dynamic_extent);
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements in a container.
|
||||
@@ -85,11 +90,11 @@ struct basic_meta_sequence_container_traits {
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] static bool clear([[maybe_unused]] void *container) {
|
||||
if constexpr(fixed_size) {
|
||||
return false;
|
||||
} else {
|
||||
if constexpr(extent == meta_dynamic_extent) {
|
||||
static_cast<Type *>(container)->clear();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,36 +120,27 @@ struct basic_meta_sequence_container_traits {
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] static bool resize([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) {
|
||||
if constexpr(fixed_size || !std::is_default_constructible_v<typename Type::value_type>) {
|
||||
return false;
|
||||
} else {
|
||||
if constexpr((extent == meta_dynamic_extent) && std::is_default_constructible_v<typename Type::value_type>) {
|
||||
static_cast<Type *>(container)->resize(sz);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a possibly const iterator to the beginning.
|
||||
* @brief Returns a possibly const iterator to the beginning or the end.
|
||||
* @param area The context to pass to the newly created iterator.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param as_const Const opaque pointer fallback.
|
||||
* @return An iterator to the first element of the container.
|
||||
* @param end False to get a pointer that is past the last element.
|
||||
* @return An iterator to the first or past the last element of the
|
||||
* container.
|
||||
*/
|
||||
static iterator begin(const meta_ctx &area, void *container, const void *as_const) {
|
||||
return (container != nullptr) ? iterator{area, static_cast<Type *>(container)->begin()}
|
||||
: iterator{area, static_cast<const Type *>(as_const)->begin()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a possibly const iterator to the end.
|
||||
* @param area The context to pass to the newly created iterator.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param as_const Const opaque pointer fallback.
|
||||
* @return An iterator that is past the last element of the container.
|
||||
*/
|
||||
static iterator end(const meta_ctx &area, void *container, const void *as_const) {
|
||||
return (container != nullptr) ? iterator{area, static_cast<Type *>(container)->end()}
|
||||
: iterator{area, static_cast<const Type *>(as_const)->end()};
|
||||
static iterator iter(const meta_ctx &area, void *container, const void *as_const, const bool end) {
|
||||
return (container == nullptr)
|
||||
? iterator{area, end ? static_cast<const Type *>(as_const)->cend() : static_cast<const Type *>(as_const)->cbegin()}
|
||||
: iterator{area, end ? static_cast<Type *>(container)->end() : static_cast<Type *>(container)->begin()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,13 +156,13 @@ struct basic_meta_sequence_container_traits {
|
||||
* @return A possibly invalid iterator to the inserted element.
|
||||
*/
|
||||
[[nodiscard]] static iterator insert([[maybe_unused]] const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const void *value, [[maybe_unused]] const void *cref, [[maybe_unused]] const iterator &it) {
|
||||
if constexpr(fixed_size) {
|
||||
return iterator{};
|
||||
} else {
|
||||
if constexpr(extent == meta_dynamic_extent) {
|
||||
auto *const non_const = any_cast<typename Type::iterator>(&it.base());
|
||||
return {area, static_cast<Type *>(container)->insert(
|
||||
non_const ? *non_const : any_cast<const typename Type::const_iterator &>(it.base()),
|
||||
(value != nullptr) ? *static_cast<const typename Type::value_type *>(value) : *static_cast<const std::remove_reference_t<typename Type::const_reference> *>(cref))};
|
||||
} else {
|
||||
return iterator{};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,11 +174,11 @@ struct basic_meta_sequence_container_traits {
|
||||
* @return A possibly invalid iterator following the last removed element.
|
||||
*/
|
||||
[[nodiscard]] static iterator erase([[maybe_unused]] const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const iterator &it) {
|
||||
if constexpr(fixed_size) {
|
||||
return iterator{};
|
||||
} else {
|
||||
if constexpr(extent == meta_dynamic_extent) {
|
||||
auto *const non_const = any_cast<typename Type::iterator>(&it.base());
|
||||
return {area, static_cast<Type *>(container)->erase(non_const ? *non_const : any_cast<const typename Type::const_iterator &>(it.base()))};
|
||||
} else {
|
||||
return iterator{};
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -193,7 +189,7 @@ struct basic_meta_sequence_container_traits {
|
||||
*/
|
||||
template<typename Type>
|
||||
struct basic_meta_associative_container_traits {
|
||||
static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Unexpected type");
|
||||
static_assert(std::is_same_v<Type, std::remove_const_t<std::remove_reference_t<Type>>>, "Unexpected type");
|
||||
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename meta_associative_container::size_type;
|
||||
@@ -238,27 +234,18 @@ struct basic_meta_associative_container_traits {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a possibly const iterator to the beginning.
|
||||
* @brief Returns a possibly const iterator to the beginning or the end.
|
||||
* @param area The context to pass to the newly created iterator.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param as_const Const opaque pointer fallback.
|
||||
* @return An iterator to the first element of the container.
|
||||
* @param end False to get a pointer that is past the last element.
|
||||
* @return An iterator to the first or past the last element of the
|
||||
* container.
|
||||
*/
|
||||
static iterator begin(const meta_ctx &area, void *container, const void *as_const) {
|
||||
return (container != nullptr) ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->begin()}
|
||||
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->begin()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a possibly const iterator to the end.
|
||||
* @param area The context to pass to the newly created iterator.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param as_const Const opaque pointer fallback.
|
||||
* @return An iterator that is past the last element of the container.
|
||||
*/
|
||||
static iterator end(const meta_ctx &area, void *container, const void *as_const) {
|
||||
return (container != nullptr) ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->end()}
|
||||
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->end()};
|
||||
static iterator iter(const meta_ctx &area, void *container, const void *as_const, const bool end) {
|
||||
return (container == nullptr)
|
||||
? iterator{area, std::bool_constant<key_only>{}, end ? static_cast<const Type *>(as_const)->cend() : static_cast<const Type *>(as_const)->cbegin()}
|
||||
: iterator{area, std::bool_constant<key_only>{}, end ? static_cast<Type *>(container)->end() : static_cast<Type *>(container)->begin()};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
#ifndef ENTT_META_CTX_HPP
|
||||
#define ENTT_META_CTX_HPP
|
||||
|
||||
#include <memory>
|
||||
#include "../container/dense_map.hpp"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/utility.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
class meta_ctx;
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
struct meta_type_node;
|
||||
|
||||
struct meta_context {
|
||||
dense_map<id_type, meta_type_node, identity> value;
|
||||
dense_map<id_type, std::unique_ptr<meta_type_node>, identity> value;
|
||||
|
||||
[[nodiscard]] inline static meta_context &from(meta_ctx &ctx);
|
||||
[[nodiscard]] inline static const meta_context &from(const meta_ctx &ctx);
|
||||
[[nodiscard]] inline static meta_context &from(meta_ctx &);
|
||||
[[nodiscard]] inline static const meta_context &from(const meta_ctx &);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
+118
-152
@@ -11,10 +11,12 @@
|
||||
#include "../config/config.h"
|
||||
#include "../core/bit.hpp"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/hashed_string.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../locator/locator.hpp"
|
||||
#include "context.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "meta.hpp"
|
||||
#include "node.hpp"
|
||||
#include "policy.hpp"
|
||||
@@ -30,14 +32,18 @@ namespace internal {
|
||||
class basic_meta_factory {
|
||||
using invoke_type = std::remove_pointer_t<decltype(meta_func_node::invoke)>;
|
||||
|
||||
auto *find_member_or_assert() {
|
||||
auto *member = find_member<&meta_data_node::id>(details->data, bucket);
|
||||
[[nodiscard]] auto &fetch_node() noexcept {
|
||||
return *meta_context::from(*ctx).value[parent];
|
||||
}
|
||||
|
||||
[[nodiscard]] auto *find_member_or_assert() {
|
||||
auto *member = find_member<&meta_data_node::id>(fetch_node().details->data, bucket);
|
||||
ENTT_ASSERT(member != nullptr, "Cannot find member");
|
||||
return member;
|
||||
}
|
||||
|
||||
auto *find_overload_or_assert() {
|
||||
auto *overload = find_overload(find_member<&meta_func_node::id>(details->func, bucket), invoke);
|
||||
[[nodiscard]] auto *find_overload_or_assert() {
|
||||
auto *overload = find_overload(find_member<&meta_func_node::id>(fetch_node().details->func, bucket), invoke);
|
||||
ENTT_ASSERT(overload != nullptr, "Cannot find overload");
|
||||
return overload;
|
||||
}
|
||||
@@ -48,69 +54,75 @@ class basic_meta_factory {
|
||||
}
|
||||
|
||||
protected:
|
||||
void type(const id_type id) noexcept {
|
||||
void type(const id_type id, const char *name) noexcept {
|
||||
reset_bucket(parent);
|
||||
auto &&elem = meta_context::from(*ctx).value[parent];
|
||||
auto &elem = fetch_node();
|
||||
ENTT_ASSERT(elem.id == id || !resolve(*ctx, id), "Duplicate identifier");
|
||||
elem.name = name;
|
||||
elem.id = id;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
void insert_or_assign(Type node) {
|
||||
auto &elem = fetch_node();
|
||||
|
||||
reset_bucket(parent);
|
||||
|
||||
if constexpr(std::is_same_v<Type, meta_base_node>) {
|
||||
auto *member = find_member<&meta_base_node::type>(details->base, node.type);
|
||||
member ? (*member = node) : details->base.emplace_back(node);
|
||||
auto *member = find_member<&meta_base_node::type>(elem.details->base, node.type);
|
||||
member ? (*member = node) : elem.details->base.emplace_back(node);
|
||||
} else if constexpr(std::is_same_v<Type, meta_conv_node>) {
|
||||
auto *member = find_member<&meta_conv_node::type>(details->conv, node.type);
|
||||
member ? (*member = node) : details->conv.emplace_back(node);
|
||||
auto *member = find_member<&meta_conv_node::type>(elem.details->conv, node.type);
|
||||
member ? (*member = node) : elem.details->conv.emplace_back(node);
|
||||
} else {
|
||||
static_assert(std::is_same_v<Type, meta_ctor_node>, "Unexpected type");
|
||||
auto *member = find_member<&meta_ctor_node::id>(details->ctor, node.id);
|
||||
member ? (*member = node) : details->ctor.emplace_back(node);
|
||||
auto *member = find_member<&meta_ctor_node::id>(elem.details->ctor, node.id);
|
||||
member ? (*member = node) : elem.details->ctor.emplace_back(node);
|
||||
}
|
||||
}
|
||||
|
||||
void dtor(meta_dtor_node node) {
|
||||
reset_bucket(parent);
|
||||
meta_context::from(*ctx).value[parent].dtor = node;
|
||||
}
|
||||
|
||||
void data(meta_data_node node) {
|
||||
auto &elem = fetch_node();
|
||||
|
||||
reset_bucket(node.id);
|
||||
|
||||
if(auto *member = find_member<&meta_data_node::id>(details->data, node.id); member == nullptr) {
|
||||
details->data.emplace_back(std::move(node));
|
||||
if(auto *member = find_member<&meta_data_node::id>(elem.details->data, node.id); member == nullptr) {
|
||||
elem.details->data.emplace_back(std::move(node));
|
||||
} else if(member->set != node.set || member->get != node.get) {
|
||||
*member = std::move(node);
|
||||
}
|
||||
}
|
||||
|
||||
void func(meta_func_node node) {
|
||||
auto &elem = fetch_node();
|
||||
|
||||
reset_bucket(node.id, node.invoke);
|
||||
|
||||
if(auto *member = find_member<&meta_func_node::id>(details->func, node.id); member == nullptr) {
|
||||
details->func.emplace_back(std::move(node));
|
||||
if(auto *member = find_member<&meta_func_node::id>(elem.details->func, node.id); member == nullptr) {
|
||||
elem.details->func.emplace_back(std::move(node));
|
||||
} else if(auto *overload = find_overload(member, node.invoke); overload == nullptr) {
|
||||
while(member->next != nullptr) { member = member->next.get(); }
|
||||
member->next = std::make_shared<meta_func_node>(std::move(node));
|
||||
member->next = std::make_unique<meta_func_node>(std::move(node));
|
||||
}
|
||||
}
|
||||
|
||||
void traits(const meta_traits value) {
|
||||
void traits(const meta_traits value, const bool unset) {
|
||||
auto set_or_unset_on = [=](auto &node) {
|
||||
node.traits = (unset ? (node.traits & ~value) : (node.traits | value));
|
||||
};
|
||||
|
||||
if(bucket == parent) {
|
||||
meta_context::from(*ctx).value[bucket].traits |= value;
|
||||
set_or_unset_on(fetch_node());
|
||||
} else if(invoke == nullptr) {
|
||||
find_member_or_assert()->traits |= value;
|
||||
set_or_unset_on(*find_member_or_assert());
|
||||
} else {
|
||||
find_overload_or_assert()->traits |= value;
|
||||
set_or_unset_on(*find_overload_or_assert());
|
||||
}
|
||||
}
|
||||
|
||||
void custom(meta_custom_node node) {
|
||||
if(bucket == parent) {
|
||||
meta_context::from(*ctx).value[bucket].custom = std::move(node);
|
||||
fetch_node().custom = std::move(node);
|
||||
} else if(invoke == nullptr) {
|
||||
find_member_or_assert()->custom = std::move(node);
|
||||
} else {
|
||||
@@ -122,12 +134,9 @@ public:
|
||||
basic_meta_factory(meta_ctx &area, meta_type_node node)
|
||||
: ctx{&area},
|
||||
parent{node.info->hash()},
|
||||
bucket{parent},
|
||||
details{node.details.get()} {
|
||||
if(details == nullptr) {
|
||||
node.details = std::make_shared<meta_type_descriptor>();
|
||||
meta_context::from(*ctx).value[parent] = node;
|
||||
details = node.details.get();
|
||||
bucket{parent} {
|
||||
if(auto *curr = meta_context::from(*ctx).value.try_emplace(parent, std::make_unique<meta_type_node>(std::move(node))).first->second.get(); curr->details == nullptr) {
|
||||
curr->details = std::make_unique<meta_type_descriptor>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +145,6 @@ private:
|
||||
id_type parent{};
|
||||
id_type bucket{};
|
||||
invoke_type *invoke{};
|
||||
meta_type_descriptor *details{};
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
@@ -144,32 +152,16 @@ private:
|
||||
|
||||
/**
|
||||
* @brief Meta factory to be used for reflection purposes.
|
||||
* @tparam Type Reflected type for which the factory was created.
|
||||
* @tparam Type Type for which the factory was created.
|
||||
*/
|
||||
template<typename Type>
|
||||
class meta_factory: private internal::basic_meta_factory {
|
||||
using base_type = internal::basic_meta_factory;
|
||||
|
||||
template<typename Setter, auto Getter, typename Policy, std::size_t... Index>
|
||||
[[deprecated("use variant types or conversion support")]]
|
||||
void data(const id_type id, std::index_sequence<Index...>) noexcept {
|
||||
using data_type = std::invoke_result_t<decltype(Getter), Type &>;
|
||||
using args_type = type_list<typename meta_function_helper_t<Type, decltype(value_list_element_v<Index, Setter>)>::args_type...>;
|
||||
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
|
||||
|
||||
base_type::data(
|
||||
internal::meta_data_node{
|
||||
id,
|
||||
/* this is never static */
|
||||
(std::is_member_object_pointer_v<decltype(value_list_element_v<Index, Setter>)> && ... && std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none,
|
||||
Setter::size,
|
||||
&internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
|
||||
&meta_arg<type_list<type_list_element_t<static_cast<std::size_t>(type_list_element_t<Index, args_type>::size != 1u), type_list_element_t<Index, args_type>>...>>,
|
||||
+[](meta_handle instance, meta_any value) { return (meta_setter<Type, value_list_element_v<Index, Setter>>(*instance.operator->(), value.as_ref()) || ...); },
|
||||
&meta_getter<Type, Getter, Policy>});
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Type of object for which this factory builds a meta type. */
|
||||
using element_type = Type;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
meta_factory() noexcept
|
||||
: meta_factory{locator<meta_ctx>::value_or()} {}
|
||||
@@ -179,15 +171,25 @@ public:
|
||||
* @param area The context into which to construct meta types.
|
||||
*/
|
||||
meta_factory(meta_ctx &area) noexcept
|
||||
: internal::basic_meta_factory{area, internal::resolve<Type>(internal::meta_context::from(area))} {}
|
||||
: internal::basic_meta_factory{area, internal::setup_node_for<Type>()} {}
|
||||
|
||||
/**
|
||||
* @brief Assigns a custom unique identifier to a meta type.
|
||||
* @param name A custom unique identifier as a **string literal**.
|
||||
* @return A meta factory for the given type.
|
||||
*/
|
||||
meta_factory type(const char *name) noexcept {
|
||||
return type(hashed_string::value(name), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a custom unique identifier to a meta type.
|
||||
* @param id A custom unique identifier.
|
||||
* @param name An optional name for the type as a **string literal**.
|
||||
* @return A meta factory for the given type.
|
||||
*/
|
||||
meta_factory type(const id_type id) noexcept {
|
||||
base_type::type(id);
|
||||
meta_factory type(const id_type id, const char *name = nullptr) noexcept {
|
||||
base_type::type(id, name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -221,7 +223,7 @@ public:
|
||||
*/
|
||||
template<auto Candidate>
|
||||
auto conv() noexcept {
|
||||
using conv_type = std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>;
|
||||
using conv_type = std::remove_const_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>;
|
||||
auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, std::invoke(Candidate, *static_cast<const Type *>(instance))); };
|
||||
base_type::insert_or_assign(internal::meta_conv_node{type_id<conv_type>().hash(), op});
|
||||
return *this;
|
||||
@@ -238,7 +240,7 @@ public:
|
||||
*/
|
||||
template<typename To>
|
||||
meta_factory conv() noexcept {
|
||||
using conv_type = std::remove_cv_t<std::remove_reference_t<To>>;
|
||||
using conv_type = std::remove_const_t<std::remove_reference_t<To>>;
|
||||
auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, static_cast<To>(*static_cast<const Type *>(instance))); };
|
||||
base_type::insert_or_assign(internal::meta_conv_node{type_id<conv_type>().hash(), op});
|
||||
return *this;
|
||||
@@ -257,11 +259,11 @@ public:
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<auto Candidate, typename Policy = as_is_t>
|
||||
template<auto Candidate, typename Policy = as_value_t>
|
||||
meta_factory ctor() noexcept {
|
||||
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
|
||||
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
|
||||
static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>, Type>, "The function doesn't return an object of the required type");
|
||||
static_assert(std::is_same_v<std::remove_const_t<std::remove_reference_t<typename descriptor::return_type>>, Type>, "The function doesn't return an object of the required type");
|
||||
base_type::insert_or_assign(internal::meta_ctor_node{type_id<typename descriptor::args_type>().hash(), descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Candidate, Policy>});
|
||||
return *this;
|
||||
}
|
||||
@@ -288,29 +290,15 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a meta destructor to a meta type.
|
||||
*
|
||||
* Both free functions and member functions can be assigned to meta types in
|
||||
* the role of destructors.<br/>
|
||||
* The signature of a free function should be identical to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(Type &);
|
||||
* @endcode
|
||||
*
|
||||
* Member functions should not take arguments instead.<br/>
|
||||
* 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.
|
||||
* @brief Assigns a meta data to a meta type.
|
||||
* @tparam Data The actual variable to attach to the meta type.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param name A custom unique identifier as a **string literal**.
|
||||
* @return A meta factory for the given type.
|
||||
*/
|
||||
template<auto Func>
|
||||
meta_factory dtor() noexcept {
|
||||
static_assert(std::is_invocable_v<decltype(Func), Type &>, "The function doesn't accept an object of the type provided");
|
||||
auto *const op = +[](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); };
|
||||
base_type::dtor(internal::meta_dtor_node{op});
|
||||
return *this;
|
||||
template<auto Data, typename Policy = as_value_t>
|
||||
meta_factory data(const char *name) noexcept {
|
||||
return data<Data, Policy>(hashed_string::value(name), name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -324,10 +312,11 @@ public:
|
||||
* @tparam Data The actual variable to attach to the meta type.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param id Unique identifier.
|
||||
* @param name An optional name for the meta data as a **string literal**.
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<auto Data, typename Policy = as_is_t>
|
||||
meta_factory data(const id_type id) noexcept {
|
||||
template<auto Data, typename Policy = as_value_t>
|
||||
meta_factory data(const id_type id, const char *name = nullptr) noexcept {
|
||||
if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
|
||||
using data_type = std::invoke_result_t<decltype(Data), Type &>;
|
||||
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
|
||||
@@ -335,11 +324,12 @@ public:
|
||||
base_type::data(
|
||||
internal::meta_data_node{
|
||||
id,
|
||||
name,
|
||||
/* this is never static */
|
||||
std::is_const_v<std::remove_reference_t<data_type>> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
|
||||
1u,
|
||||
&internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
|
||||
&meta_arg<type_list<std::remove_cv_t<std::remove_reference_t<data_type>>>>,
|
||||
&internal::resolve<std::remove_const_t<std::remove_reference_t<data_type>>>,
|
||||
&meta_arg<type_list<std::remove_const_t<std::remove_reference_t<data_type>>>>,
|
||||
&meta_setter<Type, Data>,
|
||||
&meta_getter<Type, Data, Policy>});
|
||||
} else {
|
||||
@@ -354,10 +344,11 @@ public:
|
||||
base_type::data(
|
||||
internal::meta_data_node{
|
||||
id,
|
||||
name,
|
||||
((!std::is_pointer_v<decltype(Data)> || std::is_const_v<data_type>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static,
|
||||
1u,
|
||||
&internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
|
||||
&meta_arg<type_list<std::remove_cv_t<std::remove_reference_t<data_type>>>>,
|
||||
&internal::resolve<std::remove_const_t<std::remove_reference_t<data_type>>>,
|
||||
&meta_arg<type_list<std::remove_const_t<std::remove_reference_t<data_type>>>>,
|
||||
&meta_setter<Type, Data>,
|
||||
&meta_getter<Type, Data, Policy>});
|
||||
}
|
||||
@@ -365,6 +356,20 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a meta data to a meta type by means of its setter and
|
||||
* getter.
|
||||
* @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 name A custom unique identifier as a **string literal**.
|
||||
* @return A meta factory for the given type.
|
||||
*/
|
||||
template<auto Setter, auto Getter, typename Policy = as_value_t>
|
||||
meta_factory data(const char *name) noexcept {
|
||||
return data<Setter, Getter, Policy>(hashed_string::value(name), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a meta data to a meta type by means of its setter and
|
||||
* getter.
|
||||
@@ -383,10 +388,11 @@ public:
|
||||
* @tparam Getter The actual function to use as a getter.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param id Unique identifier.
|
||||
* @param name An optional name for the meta data as a **string literal**.
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<auto Setter, auto Getter, typename Policy = as_is_t>
|
||||
meta_factory data(const id_type id) noexcept {
|
||||
template<auto Setter, auto Getter, typename Policy = as_value_t>
|
||||
meta_factory data(const id_type id, const char *name = nullptr) noexcept {
|
||||
using descriptor = meta_function_helper_t<Type, decltype(Getter)>;
|
||||
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
|
||||
|
||||
@@ -394,10 +400,11 @@ public:
|
||||
base_type::data(
|
||||
internal::meta_data_node{
|
||||
id,
|
||||
name,
|
||||
/* this is never static */
|
||||
internal::meta_traits::is_const,
|
||||
0u,
|
||||
&internal::resolve<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>>,
|
||||
&internal::resolve<std::remove_const_t<std::remove_reference_t<typename descriptor::return_type>>>,
|
||||
&meta_arg<type_list<>>,
|
||||
&meta_setter<Type, Setter>,
|
||||
&meta_getter<Type, Getter, Policy>});
|
||||
@@ -407,10 +414,11 @@ public:
|
||||
base_type::data(
|
||||
internal::meta_data_node{
|
||||
id,
|
||||
name,
|
||||
/* this is never static nor const */
|
||||
internal::meta_traits::is_none,
|
||||
1u,
|
||||
&internal::resolve<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>>,
|
||||
&internal::resolve<std::remove_const_t<std::remove_reference_t<typename descriptor::return_type>>>,
|
||||
&meta_arg<type_list<type_list_element_t<static_cast<std::size_t>(args_type::size != 1u), args_type>>>,
|
||||
&meta_setter<Type, Setter>,
|
||||
&meta_getter<Type, Getter, Policy>});
|
||||
@@ -420,27 +428,15 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a meta data to a meta type by means of its setters and
|
||||
* getter.
|
||||
*
|
||||
* Multi-setter support for meta data members. All setters are tried in the
|
||||
* order of definition before returning to the caller.<br/>
|
||||
* Setters can be either free functions, member functions or a mix of them
|
||||
* and are provided via a `value_list` type.
|
||||
*
|
||||
* @sa data
|
||||
*
|
||||
* @tparam Setter The actual functions to use as setters.
|
||||
* @tparam Getter The actual getter function.
|
||||
* @brief Assigns a meta function to a meta type.
|
||||
* @tparam Candidate The actual function to attach to the meta function.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param id Unique identifier.
|
||||
* @return A meta factory for the parent type.
|
||||
* @param name A custom unique identifier as a **string literal**.
|
||||
* @return A meta factory for the given type.
|
||||
*/
|
||||
template<typename Setter, auto Getter, typename Policy = as_is_t>
|
||||
[[deprecated("use variant types or conversion support")]]
|
||||
meta_factory data(const id_type id) noexcept {
|
||||
data<Setter, Getter, Policy>(id, std::make_index_sequence<Setter::size>{});
|
||||
return *this;
|
||||
template<auto Candidate, typename Policy = as_value_t>
|
||||
meta_factory func(const char *name) noexcept {
|
||||
return func<Candidate, Policy>(hashed_string::value(name), name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -454,19 +450,21 @@ public:
|
||||
* @tparam Candidate The actual function to attach to the meta type.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param id Unique identifier.
|
||||
* @param name An optional name for the function as a **string literal**.
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<auto Candidate, typename Policy = as_is_t>
|
||||
meta_factory func(const id_type id) noexcept {
|
||||
template<auto Candidate, typename Policy = as_value_t>
|
||||
meta_factory func(const id_type id, const char *name = nullptr) noexcept {
|
||||
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
|
||||
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
|
||||
|
||||
base_type::func(
|
||||
internal::meta_func_node{
|
||||
id,
|
||||
name,
|
||||
(descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none),
|
||||
descriptor::args_type::size,
|
||||
&internal::resolve<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>>>,
|
||||
&internal::resolve<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, std::remove_const_t<std::remove_reference_t<typename descriptor::return_type>>>>,
|
||||
&meta_arg<typename descriptor::args_type>,
|
||||
&meta_invoke<Type, Candidate, Policy>});
|
||||
|
||||
@@ -480,12 +478,13 @@ public:
|
||||
*
|
||||
* @tparam Value Type of the traits value.
|
||||
* @param value Traits value.
|
||||
* @param unset True to unset the given traits, false otherwise.
|
||||
* @return A meta factory for the parent type.
|
||||
*/
|
||||
template<typename Value>
|
||||
meta_factory traits(const Value value) {
|
||||
meta_factory traits(const Value value, const bool unset = false) {
|
||||
static_assert(std::is_enum_v<Value>, "Invalid enum type");
|
||||
base_type::traits(internal::user_to_meta_traits(value));
|
||||
base_type::traits(internal::user_to_meta_traits(value), unset);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -503,39 +502,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @param ctx The context into which to construct meta types.
|
||||
* @return A meta factory for the given type.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] [[deprecated("use meta_factory directly instead")]] auto meta(meta_ctx &ctx) noexcept {
|
||||
return meta_factory<Type>{ctx};
|
||||
}
|
||||
|
||||
/**
|
||||
* @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]] [[deprecated("use meta_factory directly instead")]] auto meta() noexcept {
|
||||
return meta<Type>(locator<meta_ctx>::value_or());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resets a type and all its parts.
|
||||
*
|
||||
@@ -549,10 +515,10 @@ template<typename Type>
|
||||
* @param ctx The context from which to reset meta types.
|
||||
*/
|
||||
inline void meta_reset(meta_ctx &ctx, const id_type id) noexcept {
|
||||
auto &&context = internal::meta_context::from(ctx);
|
||||
auto &context = internal::meta_context::from(ctx);
|
||||
|
||||
for(auto it = context.value.begin(); it != context.value.end();) {
|
||||
if(it->second.id == id) {
|
||||
if(it->second->id == id) {
|
||||
it = context.value.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
|
||||
+14
-3
@@ -1,24 +1,35 @@
|
||||
#ifndef ENTT_META_FWD_HPP
|
||||
#define ENTT_META_FWD_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
|
||||
namespace entt {
|
||||
|
||||
class meta_ctx;
|
||||
|
||||
class meta_sequence_container;
|
||||
|
||||
class meta_associative_container;
|
||||
|
||||
class meta_any;
|
||||
|
||||
struct meta_handle;
|
||||
class meta_handle;
|
||||
|
||||
struct meta_custom;
|
||||
|
||||
struct meta_data;
|
||||
class meta_data;
|
||||
|
||||
struct meta_func;
|
||||
class meta_func;
|
||||
|
||||
class meta_type;
|
||||
|
||||
template<typename>
|
||||
class meta_factory;
|
||||
|
||||
/*! @brief Used to identicate that a sequence container has not a fixed size. */
|
||||
inline constexpr std::size_t meta_dynamic_extent = (std::numeric_limits<std::size_t>::max)();
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
|
||||
+377
-360
File diff suppressed because it is too large
Load Diff
+52
-85
@@ -1,13 +1,13 @@
|
||||
#ifndef ENTT_META_NODE_HPP
|
||||
#define ENTT_META_NODE_HPP
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/attribute.h"
|
||||
#include "../core/bit.hpp"
|
||||
#include "../core/enum.hpp"
|
||||
#include "../core/fwd.hpp"
|
||||
@@ -21,7 +21,7 @@ namespace entt {
|
||||
|
||||
class meta_any;
|
||||
class meta_type;
|
||||
struct meta_handle;
|
||||
class meta_handle;
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
@@ -64,12 +64,12 @@ struct meta_type_node;
|
||||
|
||||
struct meta_custom_node {
|
||||
id_type type{};
|
||||
std::shared_ptr<void> value;
|
||||
std::shared_ptr<void> value{};
|
||||
};
|
||||
|
||||
struct meta_base_node {
|
||||
id_type type{};
|
||||
meta_type_node (*resolve)(const meta_context &) noexcept {};
|
||||
const meta_type_node &(*resolve)(const meta_context &) noexcept {};
|
||||
const void *(*cast)(const void *) noexcept {};
|
||||
};
|
||||
|
||||
@@ -87,17 +87,14 @@ struct meta_ctor_node {
|
||||
meta_any (*invoke)(const meta_ctx &, meta_any *const){};
|
||||
};
|
||||
|
||||
struct meta_dtor_node {
|
||||
void (*dtor)(void *){};
|
||||
};
|
||||
|
||||
struct meta_data_node {
|
||||
using size_type = std::size_t;
|
||||
|
||||
id_type id{};
|
||||
const char *name{};
|
||||
meta_traits traits{meta_traits::is_none};
|
||||
size_type arity{0u};
|
||||
meta_type_node (*type)(const meta_context &) noexcept {};
|
||||
const meta_type_node &(*type)(const meta_context &) noexcept {};
|
||||
meta_type (*arg)(const meta_ctx &, const size_type) noexcept {};
|
||||
bool (*set)(meta_handle, meta_any){};
|
||||
meta_any (*get)(meta_handle){};
|
||||
@@ -108,12 +105,13 @@ struct meta_func_node {
|
||||
using size_type = std::size_t;
|
||||
|
||||
id_type id{};
|
||||
const char *name{};
|
||||
meta_traits traits{meta_traits::is_none};
|
||||
size_type arity{0u};
|
||||
meta_type_node (*ret)(const meta_context &) noexcept {};
|
||||
const meta_type_node &(*ret)(const meta_context &) noexcept {};
|
||||
meta_type (*arg)(const meta_ctx &, const size_type) noexcept {};
|
||||
meta_any (*invoke)(meta_handle, meta_any *const){};
|
||||
std::shared_ptr<meta_func_node> next;
|
||||
std::unique_ptr<meta_func_node> next;
|
||||
meta_custom_node custom{};
|
||||
};
|
||||
|
||||
@@ -121,16 +119,16 @@ struct meta_template_node {
|
||||
using size_type = std::size_t;
|
||||
|
||||
size_type arity{0u};
|
||||
meta_type_node (*resolve)(const meta_context &) noexcept {};
|
||||
meta_type_node (*arg)(const meta_context &, const size_type) noexcept {};
|
||||
const meta_type_node &(*resolve)(const meta_context &) noexcept {};
|
||||
const meta_type_node &(*arg)(const meta_context &, const size_type) noexcept {};
|
||||
};
|
||||
|
||||
struct meta_type_descriptor {
|
||||
std::vector<meta_ctor_node> ctor;
|
||||
std::vector<meta_base_node> base;
|
||||
std::vector<meta_conv_node> conv;
|
||||
std::vector<meta_data_node> data;
|
||||
std::vector<meta_func_node> func;
|
||||
std::vector<meta_ctor_node> ctor{};
|
||||
std::vector<meta_base_node> base{};
|
||||
std::vector<meta_conv_node> conv{};
|
||||
std::vector<meta_data_node> data{};
|
||||
std::vector<meta_func_node> func{};
|
||||
};
|
||||
|
||||
struct meta_type_node {
|
||||
@@ -138,17 +136,16 @@ struct meta_type_node {
|
||||
|
||||
const type_info *info{};
|
||||
id_type id{};
|
||||
const char *name{};
|
||||
meta_traits traits{meta_traits::is_none};
|
||||
size_type size_of{0u};
|
||||
meta_type_node (*resolve)(const meta_context &) noexcept {};
|
||||
meta_type_node (*remove_pointer)(const meta_context &) noexcept {};
|
||||
const meta_type_node &(*remove_pointer)(const meta_context &) noexcept {};
|
||||
meta_any (*default_constructor)(const meta_ctx &){};
|
||||
double (*conversion_helper)(void *, const void *){};
|
||||
meta_any (*from_void)(const meta_ctx &, void *, const void *){};
|
||||
meta_template_node templ{};
|
||||
meta_dtor_node dtor{};
|
||||
meta_custom_node custom{};
|
||||
std::shared_ptr<meta_type_descriptor> details;
|
||||
std::unique_ptr<meta_type_descriptor> details{};
|
||||
};
|
||||
|
||||
template<auto Member, typename Type, typename Value>
|
||||
@@ -168,7 +165,7 @@ template<auto Member, typename Type, typename Value>
|
||||
}
|
||||
|
||||
template<auto Member>
|
||||
[[nodiscard]] auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id) {
|
||||
[[nodiscard]] auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id, bool recursive) {
|
||||
using value_type = typename std::remove_reference_t<decltype((node.details.get()->*Member))>::value_type;
|
||||
|
||||
if(node.details) {
|
||||
@@ -176,9 +173,11 @@ template<auto Member>
|
||||
return member;
|
||||
}
|
||||
|
||||
for(auto &&curr: node.details->base) {
|
||||
if(auto *elem = look_for<Member>(context, curr.resolve(context), id); elem) {
|
||||
return elem;
|
||||
if(recursive) {
|
||||
for(auto &&curr: node.details->base) {
|
||||
if(auto *elem = look_for<Member>(context, curr.resolve(context), id, recursive); elem) {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,29 +186,22 @@ template<auto Member>
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
meta_type_node resolve(const meta_context &) noexcept;
|
||||
const meta_type_node &resolve(const meta_context &) noexcept;
|
||||
|
||||
template<typename... Args>
|
||||
[[nodiscard]] auto meta_arg_node(const meta_context &context, type_list<Args...>, [[maybe_unused]] const std::size_t index) noexcept {
|
||||
meta_type_node (*value)(const meta_context &) noexcept = nullptr;
|
||||
|
||||
if constexpr(sizeof...(Args) != 0u) {
|
||||
std::size_t pos{};
|
||||
((value = (pos++ == index ? &resolve<std::remove_cv_t<std::remove_reference_t<Args>>> : value)), ...);
|
||||
}
|
||||
|
||||
ENTT_ASSERT(value != nullptr, "Out of bounds");
|
||||
return value(context);
|
||||
[[nodiscard]] const meta_type_node &meta_arg_node(const meta_context &context, type_list<Args...>, const std::size_t index) noexcept {
|
||||
using resolve_type = const meta_type_node &(*)(const meta_context &) noexcept;
|
||||
constexpr std::array<resolve_type, sizeof...(Args)> list{&resolve<std::remove_const_t<std::remove_reference_t<Args>>>...};
|
||||
ENTT_ASSERT(index < sizeof...(Args), "Out of bounds");
|
||||
return list[index](context);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const type_info &to, const void *instance) noexcept {
|
||||
if((from.info != nullptr) && *from.info == to) {
|
||||
return instance;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const id_type to, const void *instance) noexcept {
|
||||
if(from.details) {
|
||||
for(auto &&curr: from.details->base) {
|
||||
if(const void *elem = try_cast(context, curr.resolve(context), to, curr.cast(instance)); elem) {
|
||||
if(const void *other = curr.cast(instance); curr.type == to) {
|
||||
return other;
|
||||
} else if(const void *elem = try_cast(context, curr.resolve(context), to, other); elem) {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
@@ -218,49 +210,12 @@ template<typename... Args>
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
[[nodiscard]] inline auto try_convert(const meta_context &context, const meta_type_node &from, const type_info &to, const bool arithmetic_or_enum, const void *instance, Func func) {
|
||||
if(from.info && *from.info == to) {
|
||||
return func(instance, from);
|
||||
}
|
||||
|
||||
if(from.details) {
|
||||
for(auto &&elem: from.details->conv) {
|
||||
if(elem.type == to.hash()) {
|
||||
return func(instance, elem);
|
||||
}
|
||||
}
|
||||
|
||||
for(auto &&curr: from.details->base) {
|
||||
if(auto other = try_convert(context, curr.resolve(context), to, arithmetic_or_enum, curr.cast(instance), func); other) {
|
||||
return other;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(from.conversion_helper && arithmetic_or_enum) {
|
||||
return func(instance, from.conversion_helper);
|
||||
}
|
||||
|
||||
return func(instance);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept {
|
||||
const auto it = context.value.find(info.hash());
|
||||
return it != context.value.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] meta_type_node resolve(const meta_context &context) noexcept {
|
||||
static_assert(std::is_same_v<Type, std::remove_const_t<std::remove_reference_t<Type>>>, "Invalid type");
|
||||
|
||||
if(auto *elem = try_resolve(context, type_id<Type>()); elem) {
|
||||
return *elem;
|
||||
}
|
||||
|
||||
auto setup_node_for() noexcept {
|
||||
meta_type_node node{
|
||||
&type_id<Type>(),
|
||||
type_id<Type>().hash(),
|
||||
nullptr,
|
||||
(std::is_arithmetic_v<Type> ? meta_traits::is_arithmetic : meta_traits::is_none)
|
||||
| (std::is_integral_v<Type> ? meta_traits::is_integral : meta_traits::is_none)
|
||||
| (std::is_signed_v<Type> ? meta_traits::is_signed : meta_traits::is_none)
|
||||
@@ -272,8 +227,7 @@ template<typename Type>
|
||||
| (is_complete_v<meta_sequence_container_traits<Type>> ? meta_traits::is_sequence_container : meta_traits::is_none)
|
||||
| (is_complete_v<meta_associative_container_traits<Type>> ? meta_traits::is_associative_container : meta_traits::is_none),
|
||||
size_of_v<Type>,
|
||||
&resolve<Type>,
|
||||
&resolve<std::remove_cv_t<std::remove_pointer_t<Type>>>};
|
||||
&resolve<std::remove_const_t<std::remove_pointer_t<Type>>>};
|
||||
|
||||
if constexpr(std::is_default_constructible_v<Type>) {
|
||||
node.default_constructor = +[](const meta_ctx &ctx) {
|
||||
@@ -310,12 +264,25 @@ template<typename Type>
|
||||
node.templ = meta_template_node{
|
||||
meta_template_traits<Type>::args_type::size,
|
||||
&resolve<typename meta_template_traits<Type>::class_type>,
|
||||
+[](const meta_context &area, const std::size_t index) noexcept { return meta_arg_node(area, typename meta_template_traits<Type>::args_type{}, index); }};
|
||||
+[](const meta_context &area, const std::size_t index) noexcept -> decltype(auto) { return meta_arg_node(area, typename meta_template_traits<Type>::args_type{}, index); }};
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept {
|
||||
const auto it = context.value.find(info.hash());
|
||||
return (it != context.value.end()) ? it->second.get() : nullptr;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] const meta_type_node &resolve(const meta_context &context) noexcept {
|
||||
static_assert(std::is_same_v<Type, std::remove_const_t<std::remove_reference_t<Type>>>, "Invalid type");
|
||||
static const meta_type_node node = setup_node_for<Type>();
|
||||
const auto *elem = try_resolve(context, *node.info);
|
||||
return (elem == nullptr) ? node : *elem;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
|
||||
+33
-17
@@ -5,24 +5,16 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @brief Empty class type used to request the _as ref_ policy. */
|
||||
struct as_ref_t final {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
template<typename Type>
|
||||
static constexpr bool value = std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>;
|
||||
/*! @endcond */
|
||||
};
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
/*! @brief Empty class type used to request the _as cref_ policy. */
|
||||
struct as_cref_t final {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
template<typename Type>
|
||||
static constexpr bool value = std::is_reference_v<Type>;
|
||||
/*! @endcond */
|
||||
};
|
||||
struct meta_policy {};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/*! @brief Empty class type used to request the _as-is_ policy. */
|
||||
struct as_is_t final {
|
||||
struct as_value_t final: private internal::meta_policy {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
template<typename>
|
||||
static constexpr bool value = true;
|
||||
@@ -30,7 +22,31 @@ struct as_is_t final {
|
||||
};
|
||||
|
||||
/*! @brief Empty class type used to request the _as void_ policy. */
|
||||
struct as_void_t final {
|
||||
struct as_void_t final: private internal::meta_policy {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
template<typename>
|
||||
static constexpr bool value = true;
|
||||
/*! @endcond */
|
||||
};
|
||||
|
||||
/*! @brief Empty class type used to request the _as ref_ policy. */
|
||||
struct as_ref_t final: private internal::meta_policy {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
template<typename Type>
|
||||
static constexpr bool value = std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>;
|
||||
/*! @endcond */
|
||||
};
|
||||
|
||||
/*! @brief Empty class type used to request the _as cref_ policy. */
|
||||
struct as_cref_t final: private internal::meta_policy {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
template<typename Type>
|
||||
static constexpr bool value = std::is_reference_v<Type>;
|
||||
/*! @endcond */
|
||||
};
|
||||
|
||||
/*! @brief Empty class type used to request the _as auto_ policy. */
|
||||
struct as_is_t final: private internal::meta_policy {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
template<typename>
|
||||
static constexpr bool value = true;
|
||||
@@ -44,7 +60,7 @@ struct as_void_t final {
|
||||
*/
|
||||
template<typename Type>
|
||||
struct is_meta_policy
|
||||
: std::bool_constant<std::is_same_v<Type, as_ref_t> || std::is_same_v<Type, as_cref_t> || std::is_same_v<Type, as_is_t> || std::is_same_v<Type, as_void_t>> {};
|
||||
: std::bool_constant<std::is_base_of_v<internal::meta_policy, Type>> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
|
||||
@@ -70,7 +70,7 @@ struct meta_range_iterator final {
|
||||
|
||||
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
|
||||
if constexpr(std::is_same_v<It, typename decltype(meta_context::value)::const_iterator>) {
|
||||
return {it[value].first, Type{*ctx, it[value].second}};
|
||||
return {it[value].first, Type{*ctx, *it[value].second}};
|
||||
} else if constexpr(std::is_same_v<typename std::iterator_traits<It>::value_type, meta_base_node>) {
|
||||
return {it[value].type, Type{*ctx, it[value]}};
|
||||
} else {
|
||||
|
||||
@@ -19,8 +19,8 @@ namespace entt {
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] meta_type resolve(const meta_ctx &ctx) noexcept {
|
||||
auto &&context = internal::meta_context::from(ctx);
|
||||
return {ctx, internal::resolve<std::remove_cv_t<std::remove_reference_t<Type>>>(context)};
|
||||
const auto &context = internal::meta_context::from(ctx);
|
||||
return {ctx, internal::resolve<std::remove_const_t<std::remove_reference_t<Type>>>(context)};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,7 +39,7 @@ template<typename Type>
|
||||
* @return An iterable range to use to visit all meta types.
|
||||
*/
|
||||
[[nodiscard]] inline meta_range<meta_type, typename decltype(internal::meta_context::value)::const_iterator> resolve(const meta_ctx &ctx) noexcept {
|
||||
auto &&context = internal::meta_context::from(ctx);
|
||||
const auto &context = internal::meta_context::from(ctx);
|
||||
return {{ctx, context.value.cbegin()}, {ctx, context.value.cend()}};
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ template<typename Type>
|
||||
* @return The meta type associated with the given type info object, if any.
|
||||
*/
|
||||
[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const type_info &info) noexcept {
|
||||
auto &&context = internal::meta_context::from(ctx);
|
||||
const auto &context = internal::meta_context::from(ctx);
|
||||
const auto *elem = internal::try_resolve(context, info);
|
||||
return (elem != nullptr) ? meta_type{ctx, *elem} : meta_type{};
|
||||
}
|
||||
|
||||
+34
-79
@@ -93,11 +93,11 @@ struct meta_function_descriptor<Type, Ret (*)(MaybeType, Args...)>
|
||||
: meta_function_descriptor_traits<
|
||||
Ret,
|
||||
std::conditional_t<
|
||||
std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>,
|
||||
std::is_same_v<std::remove_const_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_const_t<std::remove_reference_t<MaybeType>>, Type>,
|
||||
type_list<Args...>,
|
||||
type_list<MaybeType, Args...>>,
|
||||
!(std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>),
|
||||
std::is_const_v<std::remove_reference_t<MaybeType>> && (std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>)> {};
|
||||
!(std::is_same_v<std::remove_const_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_const_t<std::remove_reference_t<MaybeType>>, Type>),
|
||||
std::is_const_v<std::remove_reference_t<MaybeType>> && (std::is_same_v<std::remove_const_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_const_t<std::remove_reference_t<MaybeType>>, Type>)> {};
|
||||
|
||||
/**
|
||||
* @brief Meta function descriptor.
|
||||
@@ -164,15 +164,15 @@ using meta_function_helper_t = typename meta_function_helper<Type, Candidate>::t
|
||||
* @param value Value to wrap.
|
||||
* @return A meta any containing the returned value, if any.
|
||||
*/
|
||||
template<typename Policy = as_is_t, typename Type>
|
||||
template<typename Policy = as_value_t, typename Type>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) {
|
||||
if constexpr(std::is_same_v<Policy, as_void_t>) {
|
||||
return meta_any{ctx, std::in_place_type<void>};
|
||||
} else if constexpr(std::is_same_v<Policy, as_ref_t>) {
|
||||
return meta_any{ctx, std::in_place_type<Type>, value};
|
||||
} else if constexpr(std::is_same_v<Policy, as_cref_t>) {
|
||||
if constexpr(std::is_same_v<Policy, as_cref_t>) {
|
||||
static_assert(std::is_lvalue_reference_v<Type>, "Invalid type");
|
||||
return meta_any{ctx, std::in_place_type<const std::remove_reference_t<Type> &>, std::as_const(value)};
|
||||
} else if constexpr(std::is_same_v<Policy, as_ref_t> || (std::is_same_v<Policy, as_is_t> && std::is_lvalue_reference_v<Type>)) {
|
||||
return meta_any{ctx, std::in_place_type<Type>, value};
|
||||
} else if constexpr(std::is_same_v<Policy, as_void_t>) {
|
||||
return meta_any{ctx, std::in_place_type<void>};
|
||||
} else {
|
||||
return meta_any{ctx, std::forward<Type>(value)};
|
||||
}
|
||||
@@ -185,7 +185,7 @@ template<typename Policy = as_is_t, typename Type>
|
||||
* @param value Value to wrap.
|
||||
* @return A meta any containing the returned value, if any.
|
||||
*/
|
||||
template<typename Policy = as_is_t, typename Type>
|
||||
template<typename Policy = as_value_t, typename Type>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(Type &&value) {
|
||||
return meta_dispatch<Policy, Type>(locator<meta_ctx>::value_or(), std::forward<Type>(value));
|
||||
}
|
||||
@@ -204,26 +204,26 @@ template<typename Policy, typename Candidate, typename... Args>
|
||||
}
|
||||
|
||||
template<typename Type, typename Policy, typename Candidate, std::size_t... Index>
|
||||
[[nodiscard]] meta_any meta_invoke(meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args, std::index_sequence<Index...>) {
|
||||
[[nodiscard]] meta_any meta_invoke(meta_any &instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args, std::index_sequence<Index...>) {
|
||||
using descriptor = meta_function_helper_t<Type, std::remove_reference_t<Candidate>>;
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span)
|
||||
if constexpr(std::is_invocable_v<std::remove_reference_t<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 meta_invoke_with_args<Policy>(instance->context(), std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<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 meta_invoke_with_args<Policy>(instance.context(), std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
|
||||
}
|
||||
} else if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
|
||||
if(auto *const clazz = instance->try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
|
||||
return meta_invoke_with_args<Policy>(instance->context(), std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
|
||||
if(auto *const clazz = instance.try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
|
||||
return meta_invoke_with_args<Policy>(instance.context(), std::forward<Candidate>(candidate), *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 meta_invoke_with_args<Policy>(instance->context(), std::forward<Candidate>(candidate), (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
|
||||
return meta_invoke_with_args<Policy>(instance.context(), std::forward<Candidate>(candidate), (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
|
||||
}
|
||||
}
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
|
||||
return meta_any{meta_ctx_arg, instance->context()};
|
||||
return meta_any{meta_ctx_arg, instance.context()};
|
||||
}
|
||||
|
||||
template<typename Type, typename... Args, std::size_t... Index>
|
||||
@@ -248,8 +248,8 @@ template<typename Type, typename... Args, std::size_t... Index>
|
||||
* @return The meta type of the i-th element of the list of arguments.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] static meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept {
|
||||
auto &&context = internal::meta_context::from(ctx);
|
||||
[[nodiscard]] meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept {
|
||||
const auto &context = internal::meta_context::from(ctx);
|
||||
return {ctx, internal::meta_arg_node(context, Type{}, index)};
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ template<typename Type>
|
||||
* @return The meta type of the i-th element of the list of arguments.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] static meta_type meta_arg(const std::size_t index) noexcept {
|
||||
[[nodiscard]] meta_type meta_arg(const std::size_t index) noexcept {
|
||||
return meta_arg<Type>(locator<meta_ctx>::value_or(), index);
|
||||
}
|
||||
|
||||
@@ -313,10 +313,10 @@ template<typename Type, auto Data>
|
||||
* @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>
|
||||
template<typename Type, auto Data, typename Policy = as_value_t>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_getter(meta_handle instance) {
|
||||
if constexpr(std::is_member_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) {
|
||||
if constexpr(!std::is_array_v<std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>>>) {
|
||||
if constexpr(!std::is_array_v<std::remove_const_t<std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>>>) {
|
||||
if constexpr(std::is_invocable_v<decltype(Data), Type &>) {
|
||||
if(auto *clazz = instance->try_cast<Type>(); clazz) {
|
||||
return meta_dispatch<Policy>(instance->context(), std::invoke(Data, *clazz));
|
||||
@@ -342,20 +342,6 @@ template<typename Type, auto Data, typename Policy = as_is_t>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 ctx The context from which to search for meta types.
|
||||
* @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>
|
||||
[[deprecated("a context is no longer required, it is inferred from the meta_handle")]] [[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_getter(const meta_ctx &ctx, meta_handle instance) {
|
||||
return meta_getter<Type, Data, Policy>(meta_handle{ctx, std::move(instance)});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tries to _invoke_ an object given a list of erased parameters.
|
||||
* @tparam Type Reflected type to which the object to _invoke_ is associated.
|
||||
@@ -366,25 +352,9 @@ template<typename Type, auto Data, typename Policy = as_is_t>
|
||||
* @param args Parameters to use to _invoke_ the object.
|
||||
* @return A meta any containing the returned value, if any.
|
||||
*/
|
||||
template<typename Type, typename Policy = as_is_t, typename Candidate>
|
||||
template<typename Type, typename Policy = as_value_t, typename Candidate>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(meta_handle instance, Candidate &&candidate, meta_any *const args) {
|
||||
return internal::meta_invoke<Type, Policy>(std::move(instance), std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tries to _invoke_ an object given a list of erased parameters.
|
||||
* @tparam Type Reflected type to which the object to _invoke_ is associated.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param ctx The context from which to search for meta types.
|
||||
* @tparam Candidate The type of the actual object to _invoke_.
|
||||
* @param instance An opaque instance of the underlying type, if required.
|
||||
* @param candidate The actual object to _invoke_.
|
||||
* @param args Parameters to use to _invoke_ the object.
|
||||
* @return A meta any containing the returned value, if any.
|
||||
*/
|
||||
template<typename Type, typename Policy = as_is_t, typename Candidate>
|
||||
[[deprecated("a context is no longer required, it is inferred from the meta_handle")]] [[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, Candidate &&candidate, meta_any *const args) {
|
||||
return meta_invoke<Type, Policy>(meta_handle{ctx, std::move(instance)}, std::forward<Candidate>(candidate), args);
|
||||
return internal::meta_invoke<Type, Policy>(*instance.operator->(), std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -396,24 +366,9 @@ template<typename Type, typename Policy = as_is_t, typename Candidate>
|
||||
* @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>
|
||||
template<typename Type, auto Candidate, typename Policy = as_value_t>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(meta_handle instance, meta_any *const args) {
|
||||
return internal::meta_invoke<Type, Policy>(std::move(instance), Candidate, args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tries to invoke a function given a list of erased parameters.
|
||||
* @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).
|
||||
* @param ctx The context from which to search for meta types.
|
||||
* @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>
|
||||
[[deprecated("a context is no longer required, it is inferred from the meta_handle")]] [[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, meta_any *const args) {
|
||||
return meta_invoke<Type, Candidate, Policy>(meta_handle{ctx, std::move(instance)}, args);
|
||||
return internal::meta_invoke<Type, Policy>(*instance.operator->(), Candidate, args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -461,14 +416,14 @@ template<typename Type, typename... Args>
|
||||
* @param args Parameters to use to _invoke_ the object.
|
||||
* @return A meta any containing the returned value, if any.
|
||||
*/
|
||||
template<typename Type, typename Policy = as_is_t, typename Candidate>
|
||||
template<typename Type, typename Policy = as_value_t, typename Candidate>
|
||||
[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, Candidate &&candidate, meta_any *const args) {
|
||||
if constexpr(meta_function_helper_t<Type, Candidate>::is_static || std::is_class_v<std::remove_cv_t<std::remove_reference_t<Candidate>>>) {
|
||||
return internal::meta_invoke<Type, Policy>(meta_handle{meta_ctx_arg, ctx}, std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
|
||||
if constexpr(meta_function_helper_t<Type, Candidate>::is_static || std::is_class_v<std::remove_const_t<std::remove_reference_t<Candidate>>>) {
|
||||
meta_any placeholder{meta_ctx_arg, ctx};
|
||||
return internal::meta_invoke<Type, Policy>(placeholder, std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
|
||||
} else {
|
||||
meta_any handle{ctx, args->as_ref()};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span)
|
||||
return internal::meta_invoke<Type, Policy>(handle, std::forward<Candidate>(candidate), args + 1u, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
|
||||
return internal::meta_invoke<Type, Policy>(*args, std::forward<Candidate>(candidate), args + 1u, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,7 +436,7 @@ template<typename Type, typename Policy = as_is_t, typename Candidate>
|
||||
* @param args Parameters to use to _invoke_ the object.
|
||||
* @return A meta any containing the returned value, if any.
|
||||
*/
|
||||
template<typename Type, typename Policy = as_is_t, typename Candidate>
|
||||
template<typename Type, typename Policy = as_value_t, typename Candidate>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_construct(Candidate &&candidate, meta_any *const args) {
|
||||
return meta_construct<Type, Policy>(locator<meta_ctx>::value_or(), std::forward<Candidate>(candidate), args);
|
||||
}
|
||||
@@ -500,7 +455,7 @@ template<typename Type, typename Policy = as_is_t, typename Candidate>
|
||||
* @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>
|
||||
template<typename Type, auto Candidate, typename Policy = as_value_t>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_construct(const meta_ctx &ctx, meta_any *const args) {
|
||||
return meta_construct<Type, Policy>(ctx, Candidate, args);
|
||||
}
|
||||
@@ -513,7 +468,7 @@ template<typename Type, auto Candidate, typename Policy = as_is_t>
|
||||
* @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>
|
||||
template<typename Type, auto Candidate, typename Policy = as_value_t>
|
||||
[[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_construct(meta_any *const args) {
|
||||
return meta_construct<Type, Candidate, Policy>(locator<meta_ctx>::value_or(), args);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::basic_any<*>">
|
||||
<DisplayString>{{ type={ info->alias,na }, policy={ mode,en } }}</DisplayString>
|
||||
<DisplayString>{{ policy={ mode,en } }}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::compressed_pair<*>">
|
||||
<Intrinsic Name="first" Optional="true" Expression="((first_base*)this)->value"/>
|
||||
@@ -47,7 +47,7 @@
|
||||
<Intrinsic Name="is_valid_entity" Expression="!traits_type::version_mask || (*((traits_type::entity_type *)&entity) < (traits_type::version_mask << traits_type::length))">
|
||||
<Parameter Name="entity" Type="const traits_type::value_type &"/>
|
||||
</Intrinsic>
|
||||
<DisplayString>{{ size={ packed.size() }, type={ info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{ size={ packed.size() }, type={ descriptor->alias,na } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[capacity]" ExcludeView="simple">packed.capacity()</Item>
|
||||
<Item Name="[policy]" ExcludeView="simple">mode,en</Item>
|
||||
@@ -94,7 +94,7 @@
|
||||
<Intrinsic Name="is_valid_entity" Expression="!base_type::traits_type::version_mask || (*((base_type::traits_type::entity_type *)&entity) < (base_type::traits_type::version_mask << base_type::traits_type::length))">
|
||||
<Parameter Name="entity" Type="const base_type::traits_type::value_type &"/>
|
||||
</Intrinsic>
|
||||
<DisplayString>{{ size={ base_type::packed.size() }, type={ base_type::info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{ size={ base_type::packed.size() }, type={ base_type::descriptor->alias,na } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[capacity]" Optional="true" ExcludeView="simple">payload.capacity() * traits_type::page_size</Item>
|
||||
<Item Name="[page size]" Optional="true" ExcludeView="simple">traits_type::page_size</Item>
|
||||
@@ -163,7 +163,7 @@
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<If Condition="pool_at(pos)->sparse.size() > page && pool_at(pos)->sparse[page] != nullptr && ((*((traits_type::entity_type *)&pool_at(pos)->sparse[page][offset])) & entity_mask) != entity_mask">
|
||||
<Item Name="[{ pool_at(pos)->info->alias,na }:{ ((*((traits_type::entity_type *)&pool_at(pos)->sparse[page][offset])) & entity_mask) != entity_mask }]">pool_at(pos),view(simple)nanr</Item>
|
||||
<Item Name="[{ pool_at(pos)->descriptor->alias,na }:{ ((*((traits_type::entity_type *)&pool_at(pos)->sparse[page][offset])) & entity_mask) != entity_mask }]">pool_at(pos),view(simple)nanr</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
</Loop>
|
||||
@@ -34,33 +34,33 @@
|
||||
<Intrinsic Name="has_trait" Expression="!!(traits & property)">
|
||||
<Parameter Name="property" Type="int"/>
|
||||
</Intrinsic>
|
||||
<DisplayString Condition="name != nullptr">{{ id={ name,na } }}</DisplayString>
|
||||
<DisplayString Condition="get != nullptr">{{ id={ id } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[id]">id</Item>
|
||||
<Item Name="[name]" Condition="name != nullptr">name,na</Item>
|
||||
<Item Name="[arity]">arity</Item>
|
||||
<Item Name="[is_const]">has_trait(entt::internal::meta_traits::is_const)</Item>
|
||||
<Item Name="[is_static]">has_trait(entt::internal::meta_traits::is_static)</Item>
|
||||
<Item Name="[custom]">custom</Item>
|
||||
<Item Name="[custom]" Condition="custom.value != nullptr">custom</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_dtor_node">
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand/>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_func_node" >
|
||||
<Intrinsic Name="has_trait" Expression="!!(traits & property)">
|
||||
<Parameter Name="property" Type="int"/>
|
||||
</Intrinsic>
|
||||
<DisplayString Condition="name != nullptr">{{ id={ name,na } }}</DisplayString>
|
||||
<DisplayString Condition="invoke != nullptr">{{ id={ id } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[id]">id</Item>
|
||||
<Item Name="[name]" Condition="name != nullptr">name,na</Item>
|
||||
<Item Name="[arity]">arity</Item>
|
||||
<Item Name="[is_const]">has_trait(entt::internal::meta_traits::is_const)</Item>
|
||||
<Item Name="[is_static]">has_trait(entt::internal::meta_traits::is_static)</Item>
|
||||
<Item Name="[next]" Condition="next != nullptr">*next</Item>
|
||||
<Item Name="[custom]">custom</Item>
|
||||
<Item Name="[custom]" Condition="custom.value != nullptr">custom</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_template_node">
|
||||
@@ -84,10 +84,12 @@
|
||||
<Intrinsic Name="has_trait" Expression="!!(traits & property)">
|
||||
<Parameter Name="property" Type="int"/>
|
||||
</Intrinsic>
|
||||
<DisplayString Condition="name != nullptr">{{ type={ name,na } }}</DisplayString>
|
||||
<DisplayString Condition="info != nullptr">{{ type={ info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[id]">id</Item>
|
||||
<Item Name="[name]" Condition="name != nullptr">name,na</Item>
|
||||
<Item Name="[sizeof]">size_of</Item>
|
||||
<Item Name="[is_arithmetic]">has_trait(entt::internal::meta_traits::is_arithmetic)</Item>
|
||||
<Item Name="[is_integral]">has_trait(entt::internal::meta_traits::is_integral)</Item>
|
||||
@@ -102,17 +104,17 @@
|
||||
<Item Name="[default_constructor]">default_constructor != nullptr</Item>
|
||||
<Item Name="[conversion_helper]">conversion_helper != nullptr</Item>
|
||||
<Item Name="[from_void]">from_void != nullptr</Item>
|
||||
<Item Name="[template_info]">templ</Item>
|
||||
<Item Name="[custom]">custom</Item>
|
||||
<Item Name="[details]" Condition="!(details == nullptr)">*details</Item>
|
||||
<Item Name="[template_info]" Condition="templ.arity != 0u">templ</Item>
|
||||
<Item Name="[custom]" Condition="custom.value != nullptr">custom</Item>
|
||||
<Item Name="[details]" Condition="details != nullptr">*details</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_any">
|
||||
<DisplayString Condition="node.info != nullptr">{{ type={ node.info->alias,na }, policy={ storage.mode,en } }}</DisplayString>
|
||||
<DisplayString>{ storage }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>node</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
<ExpandedItem Condition="node != nullptr">node,na</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_handle">
|
||||
@@ -125,7 +127,7 @@
|
||||
<DisplayString Condition="data != nullptr">{{ const={ const_only } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
|
||||
<Item Name="[const]">const_only</Item>
|
||||
<Item Name="[data]">data</Item>
|
||||
</Expand>
|
||||
@@ -134,38 +136,40 @@
|
||||
<DisplayString Condition="data != nullptr">{{ const={ const_only } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
|
||||
<Item Name="[const]">const_only</Item>
|
||||
<Item Name="[data]">data</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_custom">
|
||||
<DisplayString>{ node }</DisplayString>
|
||||
<DisplayString Condition="node != nullptr">{ node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>node</ExpandedItem>
|
||||
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_data">
|
||||
<DisplayString Condition="node.get != nullptr">{ node }</DisplayString>
|
||||
<DisplayString Condition="node != nullptr">{ node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="node.get != nullptr">node</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_func">
|
||||
<DisplayString Condition="node.invoke != nullptr">{ node }</DisplayString>
|
||||
<DisplayString Condition="node != nullptr">{ node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="node.invoke != nullptr">node</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_type">
|
||||
<DisplayString>{ node }</DisplayString>
|
||||
<DisplayString Condition="node != nullptr">{ node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>node</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
|
||||
<Item Name="[context]" Condition="ctx != nullptr">ctx,na</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_ctx">
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::basic_process<*>">
|
||||
<DisplayString>{{ state={ current,en } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[state]">current,en</Item>
|
||||
<Item Name="[child]" Condition="next.first_base::value != nullptr">*next.first_base::value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_scheduler<*>">
|
||||
<Intrinsic Name="size" Expression="handlers.first_base::value.size()"/>
|
||||
<DisplayString>{{ size={ size() } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[capacity]" ExcludeView="simple">handlers.first_base::value.capacity()</Item>
|
||||
<IndexListItems>
|
||||
<Size>size()</Size>
|
||||
<ValueNode>*handlers.first_base::value[$i]</ValueNode>
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
+13
-8
@@ -211,23 +211,28 @@ public:
|
||||
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_cv_t<std::remove_reference_t<Type>>>()} {}
|
||||
vtable{poly_vtable<Concept, Len, Align>::template instance<std::remove_const_t<std::remove_reference_t<Type>>>()} {}
|
||||
|
||||
/**
|
||||
* @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>>>
|
||||
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, basic_poly>>>
|
||||
basic_poly(Type &&value) noexcept
|
||||
: basic_poly{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)} {}
|
||||
: basic_poly{std::in_place_type<std::remove_const_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)} {}
|
||||
|
||||
/**
|
||||
* @brief Returns the object type if any, `type_id<void>()` otherwise.
|
||||
* @return The object type if any, `type_id<void>()` otherwise.
|
||||
* @brief Returns the object type info if any, `type_id<void>()` otherwise.
|
||||
* @return The object type info if any, `type_id<void>()` otherwise.
|
||||
*/
|
||||
[[nodiscard]] const type_info &type() const noexcept {
|
||||
return storage.type();
|
||||
[[nodiscard]] const type_info &info() const noexcept {
|
||||
return storage.info();
|
||||
}
|
||||
|
||||
/*! @copydoc info */
|
||||
[[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept {
|
||||
return info();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,7 +257,7 @@ public:
|
||||
template<typename Type, typename... Args>
|
||||
void emplace(Args &&...args) {
|
||||
storage.template emplace<Type>(std::forward<Args>(args)...);
|
||||
vtable = poly_vtable<Concept, Len, Align>::template instance<std::remove_cv_t<std::remove_reference_t<Type>>>();
|
||||
vtable = poly_vtable<Concept, Len, Align>::template instance<std::remove_const_t<std::remove_reference_t<Type>>>();
|
||||
}
|
||||
|
||||
/*! @brief Destroys contained object */
|
||||
|
||||
@@ -6,14 +6,17 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
template<typename, typename>
|
||||
class process;
|
||||
template<typename, typename = std::allocator<void>>
|
||||
class basic_process;
|
||||
|
||||
template<typename = std::uint32_t, typename = std::allocator<void>>
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using process = basic_process<std::uint32_t>;
|
||||
|
||||
template<typename, typename = std::allocator<void>>
|
||||
class basic_scheduler;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using scheduler = basic_scheduler<>;
|
||||
using scheduler = basic_scheduler<std::uint32_t>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
+154
-192
@@ -2,22 +2,32 @@
|
||||
#define ENTT_PROCESS_PROCESS_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../core/compressed_pair.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename, typename, typename>
|
||||
struct process_adaptor;
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Base class for processes.
|
||||
*
|
||||
* This class stays true to the CRTP idiom. Derived classes must specify what's
|
||||
* the intended type for elapsed times.<br/>
|
||||
* A process should expose publicly the following member functions whether
|
||||
* required:
|
||||
* Derived classes must specify what's the intended type for elapsed times.<br/>
|
||||
* A process can implement the following member functions whether required:
|
||||
*
|
||||
* * @code{.cpp}
|
||||
* void update(Delta, void *);
|
||||
* void update(Delta, void *) override;
|
||||
* @endcode
|
||||
*
|
||||
* It's invoked once per tick until a process is explicitly aborted or it
|
||||
@@ -28,30 +38,21 @@ namespace entt {
|
||||
* update.
|
||||
*
|
||||
* * @code{.cpp}
|
||||
* void init();
|
||||
* @endcode
|
||||
*
|
||||
* 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();
|
||||
* void succeeded() override;
|
||||
* @endcode
|
||||
*
|
||||
* It's invoked in case of success, immediately after an update and during the
|
||||
* same tick.
|
||||
*
|
||||
* * @code{.cpp}
|
||||
* void failed();
|
||||
* void failed() override;
|
||||
* @endcode
|
||||
*
|
||||
* It's invoked in case of errors, immediately after an update and during the
|
||||
* same tick.
|
||||
*
|
||||
* * @code{.cpp}
|
||||
* void aborted();
|
||||
* void aborted() override;
|
||||
* @endcode
|
||||
*
|
||||
* It's invoked only if a process is explicitly aborted. There is no guarantee
|
||||
@@ -59,18 +60,18 @@ namespace entt {
|
||||
* process is aborted immediately or not.
|
||||
*
|
||||
* Derived classes can change the internal state of a process by invoking the
|
||||
* `succeed` and `fail` protected member functions and even pause or unpause the
|
||||
* process itself.
|
||||
* `succeed` and `fail` member functions and even pause or unpause the process
|
||||
* itself.
|
||||
*
|
||||
* @sa scheduler
|
||||
*
|
||||
* @tparam Derived Actual type of process that extends the class template.
|
||||
* @tparam Delta Type to use to provide elapsed time.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Derived, typename Delta>
|
||||
class process {
|
||||
template<typename Delta, typename Allocator>
|
||||
class basic_process: public std::enable_shared_from_this<basic_process<Delta, Allocator>> {
|
||||
enum class state : std::uint8_t {
|
||||
uninitialized = 0,
|
||||
idle = 0,
|
||||
running,
|
||||
paused,
|
||||
succeeded,
|
||||
@@ -80,45 +81,73 @@ class process {
|
||||
rejected
|
||||
};
|
||||
|
||||
template<typename Target = Derived>
|
||||
auto next(std::integral_constant<state, state::uninitialized>)
|
||||
-> decltype(std::declval<Target>().init(), void()) {
|
||||
static_cast<Target *>(this)->init();
|
||||
virtual void update(const Delta, void *) {
|
||||
abort();
|
||||
}
|
||||
|
||||
template<typename Target = Derived>
|
||||
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);
|
||||
}
|
||||
virtual void succeeded() {}
|
||||
virtual void failed() {}
|
||||
virtual void aborted() {}
|
||||
|
||||
template<typename Target = Derived>
|
||||
auto next(std::integral_constant<state, state::succeeded>)
|
||||
-> decltype(std::declval<Target>().succeeded(), void()) {
|
||||
static_cast<Target *>(this)->succeeded();
|
||||
}
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Type used to provide elapsed time. */
|
||||
using delta_type = Delta;
|
||||
/*! @brief Handle type. */
|
||||
using handle_type = std::shared_ptr<basic_process>;
|
||||
|
||||
template<typename Target = Derived>
|
||||
auto next(std::integral_constant<state, state::failed>)
|
||||
-> decltype(std::declval<Target>().failed(), void()) {
|
||||
static_cast<Target *>(this)->failed();
|
||||
}
|
||||
/*! @brief Default constructor. */
|
||||
basic_process()
|
||||
: basic_process{allocator_type{}} {}
|
||||
|
||||
template<typename Target = Derived>
|
||||
auto next(std::integral_constant<state, state::aborted>)
|
||||
-> decltype(std::declval<Target>().aborted(), void()) {
|
||||
static_cast<Target *>(this)->aborted();
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void next(Args...) const noexcept {}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Terminates a process with success if it's still alive.
|
||||
*
|
||||
* The function is idempotent and it does nothing if the process isn't
|
||||
* alive.
|
||||
* @brief Constructs a scheduler with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_process(const allocator_type &allocator)
|
||||
: next{nullptr, allocator},
|
||||
current{state::idle} {}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_process(const basic_process &) = delete;
|
||||
|
||||
/*! @brief Default move constructor, deleted on purpose. */
|
||||
basic_process(basic_process &&) = delete;
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
virtual ~basic_process() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This process scheduler.
|
||||
*/
|
||||
basic_process &operator=(const basic_process &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator, deleted on purpose.
|
||||
* @return This process scheduler.
|
||||
*/
|
||||
basic_process &operator=(basic_process &&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
||||
return next.second();
|
||||
}
|
||||
|
||||
/*! @brief Aborts a process if it's still alive, otherwise does nothing. */
|
||||
void abort() {
|
||||
if(alive()) {
|
||||
current = state::aborted;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Terminates a process with success if it's still alive, otherwise
|
||||
* does nothing.
|
||||
*/
|
||||
void succeed() noexcept {
|
||||
if(alive()) {
|
||||
@@ -127,10 +156,8 @@ protected:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Terminates a process with errors if it's still alive.
|
||||
*
|
||||
* The function is idempotent and it does nothing if the process isn't
|
||||
* alive.
|
||||
* @brief Terminates a process with errors if it's still alive, otherwise
|
||||
* does nothing.
|
||||
*/
|
||||
void fail() noexcept {
|
||||
if(alive()) {
|
||||
@@ -138,75 +165,17 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops a process if it's in a running state.
|
||||
*
|
||||
* The function is idempotent and it does nothing if the process isn't
|
||||
* running.
|
||||
*/
|
||||
/*! @brief Stops a process if it's running, otherwise does nothing. */
|
||||
void pause() noexcept {
|
||||
if(current == state::running) {
|
||||
if(alive()) {
|
||||
current = state::paused;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restarts a process if it's paused.
|
||||
*
|
||||
* The function is idempotent and it does nothing if the process isn't
|
||||
* paused.
|
||||
*/
|
||||
/*! @brief Restarts a process if it's paused, otherwise does nothing. */
|
||||
void unpause() noexcept {
|
||||
if(current == state::paused) {
|
||||
current = state::running;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Type used to provide elapsed time. */
|
||||
using delta_type = Delta;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
constexpr process() = default;
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
process(const process &) = default;
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
process(process &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator.
|
||||
* @return This process.
|
||||
*/
|
||||
process &operator=(const process &) = default;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @return This process.
|
||||
*/
|
||||
process &operator=(process &&) noexcept = default;
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
virtual ~process() {
|
||||
static_assert(std::is_base_of_v<process, Derived>, "Incorrect use of the class template");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Aborts a process if it's still alive.
|
||||
*
|
||||
* The function is idempotent and it does nothing if the process isn't
|
||||
* alive.
|
||||
*
|
||||
* @param immediate Requests an immediate operation.
|
||||
*/
|
||||
void abort(const bool immediate = false) {
|
||||
if(alive()) {
|
||||
current = state::aborted;
|
||||
|
||||
if(immediate) {
|
||||
tick({});
|
||||
}
|
||||
current = state::running;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,18 +212,50 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates a process and its internal state if required.
|
||||
* @brief Assigns a child process to run in case of success.
|
||||
* @tparam Type Type of child process to create.
|
||||
* @tparam Args Types of arguments to use to initialize the child process.
|
||||
* @param args Parameters to use to initialize the child process.
|
||||
* @return A reference to the newly created child process.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
basic_process &then(Args &&...args) {
|
||||
const auto &allocator = next.second();
|
||||
return *(next.first() = std::allocate_shared<Type>(allocator, allocator, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a child process to run in case of success.
|
||||
* @tparam Func Type of child process to create.
|
||||
* @param func Either a lambda or a functor to use as a child process.
|
||||
* @return A reference to the newly created child process.
|
||||
*/
|
||||
template<typename Func>
|
||||
basic_process &then(Func func) {
|
||||
const auto &allocator = next.second();
|
||||
using process_type = internal::process_adaptor<delta_type, Func, allocator_type>;
|
||||
return *(next.first() = std::allocate_shared<process_type>(allocator, allocator, std::move(func)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the child process without releasing ownership, if any.
|
||||
* @return The child process attached to the object, if any.
|
||||
*/
|
||||
handle_type peek() {
|
||||
return next.first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates a process and its internal state, if required.
|
||||
* @param delta Elapsed time.
|
||||
* @param data Optional data.
|
||||
*/
|
||||
void tick(const Delta delta, void *data = nullptr) {
|
||||
switch(current) {
|
||||
case state::uninitialized:
|
||||
next(std::integral_constant<state, state::uninitialized>{});
|
||||
current = state::running;
|
||||
break;
|
||||
case state::idle:
|
||||
case state::running:
|
||||
next(std::integral_constant<state, state::running>{}, delta, data);
|
||||
current = state::running;
|
||||
update(delta, data);
|
||||
break;
|
||||
default:
|
||||
// suppress warnings
|
||||
@@ -264,15 +265,15 @@ public:
|
||||
// if it's dead, it must be notified and removed immediately
|
||||
switch(current) {
|
||||
case state::succeeded:
|
||||
next(std::integral_constant<state, state::succeeded>{});
|
||||
succeeded();
|
||||
current = state::finished;
|
||||
break;
|
||||
case state::failed:
|
||||
next(std::integral_constant<state, state::failed>{});
|
||||
failed();
|
||||
current = state::rejected;
|
||||
break;
|
||||
case state::aborted:
|
||||
next(std::integral_constant<state, state::aborted>{});
|
||||
aborted();
|
||||
current = state::rejected;
|
||||
break;
|
||||
default:
|
||||
@@ -282,73 +283,34 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
state current{state::uninitialized};
|
||||
compressed_pair<handle_type, allocator_type> next;
|
||||
state current;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Adaptor for lambdas and functors to turn them into processes.
|
||||
*
|
||||
* 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 signature of the function call operator should be equivalent to the
|
||||
* following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* 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.
|
||||
*
|
||||
* The signature of the function call operator of both `succeed` and `fail`
|
||||
* is equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void();
|
||||
* @endcode
|
||||
*
|
||||
* Usually users shouldn't worry about creating adaptors. A scheduler will
|
||||
* create them internally each and avery time a lambda or a functor is used as
|
||||
* a process.
|
||||
*
|
||||
* @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 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>
|
||||
process_adaptor(Args &&...args)
|
||||
: Func{std::forward<Args>(args)...} {}
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
/**
|
||||
* @brief Updates a process and its internal state if required.
|
||||
* @param delta Elapsed time.
|
||||
* @param data Optional data.
|
||||
*/
|
||||
void update(const Delta delta, void *data) {
|
||||
Func::operator()(
|
||||
delta,
|
||||
data,
|
||||
[this]() { this->succeed(); },
|
||||
[this]() { this->fail(); });
|
||||
template<typename Delta, typename Func, typename Allocator>
|
||||
struct process_adaptor: public basic_process<Delta, Allocator> {
|
||||
using allocator_type = Allocator;
|
||||
using base_type = basic_process<Delta, Allocator>;
|
||||
using delta_type = typename base_type::delta_type;
|
||||
|
||||
process_adaptor(const allocator_type &allocator, Func proc)
|
||||
: base_type{allocator},
|
||||
func{std::move(proc)} {}
|
||||
|
||||
void update(const delta_type delta, void *data) override {
|
||||
func(*this, delta, data);
|
||||
}
|
||||
|
||||
private:
|
||||
Func func;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
|
||||
+34
-171
@@ -13,44 +13,6 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Delta>
|
||||
struct basic_process_handler {
|
||||
virtual ~basic_process_handler() = default;
|
||||
|
||||
virtual bool update(Delta, void *) = 0;
|
||||
virtual void abort(bool) = 0;
|
||||
|
||||
// std::shared_ptr because of its type erased allocator which is useful here
|
||||
std::shared_ptr<basic_process_handler> next;
|
||||
};
|
||||
|
||||
template<typename Delta, typename Type>
|
||||
struct process_handler final: basic_process_handler<Delta> {
|
||||
template<typename... Args>
|
||||
process_handler(Args &&...args)
|
||||
: process{std::forward<Args>(args)...} {}
|
||||
|
||||
bool update(const Delta delta, void *data) override {
|
||||
if(process.tick(delta, data); process.rejected()) {
|
||||
this->next.reset();
|
||||
}
|
||||
|
||||
return (process.rejected() || process.finished());
|
||||
}
|
||||
|
||||
void abort(const bool immediate) override {
|
||||
process.abort(immediate);
|
||||
}
|
||||
|
||||
Type process;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Cooperative scheduler for processes.
|
||||
*
|
||||
@@ -62,14 +24,6 @@ struct process_handler final: basic_process_handler<Delta> {
|
||||
* its child when it terminates if it returns with success. In case of errors,
|
||||
* both the process and its child are discarded.
|
||||
*
|
||||
* Example of use (pseudocode):
|
||||
*
|
||||
* @code{.cpp}
|
||||
* scheduler.attach([](auto delta, void *, auto succeed, auto fail) {
|
||||
* // code
|
||||
* }).then<my_process>(arguments...);
|
||||
* @endcode
|
||||
*
|
||||
* In order to invoke all scheduled processes, call the `update` member function
|
||||
* passing it the elapsed time to forward to the tasks.
|
||||
*
|
||||
@@ -80,17 +34,14 @@ struct process_handler final: basic_process_handler<Delta> {
|
||||
*/
|
||||
template<typename Delta, typename Allocator>
|
||||
class basic_scheduler {
|
||||
template<typename Type>
|
||||
using handler_type = internal::process_handler<Delta, Type>;
|
||||
|
||||
// std::shared_ptr because of its type erased allocator which is useful here
|
||||
using process_type = std::shared_ptr<internal::basic_process_handler<Delta>>;
|
||||
|
||||
using base_type = basic_process<Delta, Allocator>;
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
using container_allocator = typename alloc_traits::template rebind_alloc<process_type>;
|
||||
using container_type = std::vector<process_type, container_allocator>;
|
||||
using container_allocator = typename alloc_traits::template rebind_alloc<std::shared_ptr<base_type>>;
|
||||
using container_type = std::vector<std::shared_ptr<base_type>, container_allocator>;
|
||||
|
||||
public:
|
||||
/*! @brief Process type. */
|
||||
using type = base_type;
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Unsigned integer type. */
|
||||
@@ -194,122 +145,28 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Schedules a process for the next tick.
|
||||
*
|
||||
* Returned value can be used to attach a continuation for the last process.
|
||||
* The continutation is scheduled automatically when the process terminates
|
||||
* and only if the process returns with success.
|
||||
*
|
||||
* Example of use (pseudocode):
|
||||
*
|
||||
* @code{.cpp}
|
||||
* // schedules a task in the form of a process class
|
||||
* scheduler.attach<my_process>(arguments...)
|
||||
* // appends a child in the form of a lambda function
|
||||
* .then([](auto delta, void *, auto succeed, auto fail) {
|
||||
* // code
|
||||
* })
|
||||
* // appends a child in the form of another process class
|
||||
* .then<my_other_process>();
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Proc Type of process to schedule.
|
||||
* @tparam Type Type of process to create.
|
||||
* @tparam Args Types of arguments to use to initialize the process.
|
||||
* @param args Parameters to use to initialize the process.
|
||||
* @return This process scheduler.
|
||||
* @return A reference to the newly created process.
|
||||
*/
|
||||
template<typename Proc, typename... Args>
|
||||
basic_scheduler &attach(Args &&...args) {
|
||||
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
|
||||
auto &ref = handlers.first().emplace_back(std::allocate_shared<handler_type<Proc>>(handlers.second(), std::forward<Args>(args)...));
|
||||
// forces the process to exit the uninitialized state
|
||||
ref->update({}, nullptr);
|
||||
return *this;
|
||||
template<typename Type, typename... Args>
|
||||
type &attach(Args &&...args) {
|
||||
const auto &allocator = handlers.second();
|
||||
return *handlers.first().emplace_back(std::allocate_shared<Type>(allocator, allocator, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Schedules a process for the next tick.
|
||||
*
|
||||
* A process can be either a lambda or a functor. The scheduler wraps both
|
||||
* of them in a process adaptor internally.<br/>
|
||||
* The signature of the function call operator should be equivalent to the
|
||||
* following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* 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.
|
||||
*
|
||||
* The signature of the function call operator of both `succeed` and `fail`
|
||||
* is equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void();
|
||||
* @endcode
|
||||
*
|
||||
* Returned value can be used to attach a continuation for the last process.
|
||||
* The continutation is scheduled automatically when the process terminates
|
||||
* and only if the process returns with success.
|
||||
*
|
||||
* Example of use (pseudocode):
|
||||
*
|
||||
* @code{.cpp}
|
||||
* // schedules a task in the form of a lambda function
|
||||
* scheduler.attach([](auto delta, void *, auto succeed, auto fail) {
|
||||
* // code
|
||||
* })
|
||||
* // appends a child in the form of another lambda function
|
||||
* .then([](auto delta, void *, auto succeed, auto fail) {
|
||||
* // code
|
||||
* })
|
||||
* // appends a child in the form of a process class
|
||||
* .then<my_process>(arguments...);
|
||||
* @endcode
|
||||
*
|
||||
* @sa process_adaptor
|
||||
*
|
||||
* @tparam Func Type of process to schedule.
|
||||
* @tparam Func Type of process to create.
|
||||
* @param func Either a lambda or a functor to use as a process.
|
||||
* @return This process scheduler.
|
||||
* @return A reference to the newly created process.
|
||||
*/
|
||||
template<typename Func>
|
||||
basic_scheduler &attach(Func &&func) {
|
||||
using Proc = process_adaptor<std::decay_t<Func>, Delta>;
|
||||
return attach<Proc>(std::forward<Func>(func));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a process as a continuation of the last scheduled process.
|
||||
* @tparam Proc Type of process to use as a continuation.
|
||||
* @tparam Args Types of arguments to use to initialize the process.
|
||||
* @param args Parameters to use to initialize the process.
|
||||
* @return This process scheduler.
|
||||
*/
|
||||
template<typename Proc, typename... Args>
|
||||
basic_scheduler &then(Args &&...args) {
|
||||
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
|
||||
ENTT_ASSERT(!handlers.first().empty(), "Process not available");
|
||||
auto *curr = handlers.first().back().get();
|
||||
for(; curr->next; curr = curr->next.get()) {}
|
||||
curr->next = std::allocate_shared<handler_type<Proc>>(handlers.second(), std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a process as a continuation of the last scheduled process.
|
||||
* @tparam Func Type of process to use as a continuation.
|
||||
* @param func Either a lambda or a functor to use as a process.
|
||||
* @return This process scheduler.
|
||||
*/
|
||||
template<typename Func>
|
||||
basic_scheduler &then(Func &&func) {
|
||||
using Proc = process_adaptor<std::decay_t<Func>, Delta>;
|
||||
return then<Proc>(std::forward<Func>(func));
|
||||
type &attach(Func func) {
|
||||
const auto &allocator = handlers.second();
|
||||
using process_type = internal::process_adaptor<delta_type, Func, allocator_type>;
|
||||
return *handlers.first().emplace_back(std::allocate_shared<process_type>(allocator, allocator, std::move(func)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -325,16 +182,18 @@ public:
|
||||
*/
|
||||
void update(const delta_type delta, void *data = nullptr) {
|
||||
for(auto next = handlers.first().size(); next; --next) {
|
||||
if(const auto pos = next - 1u; handlers.first()[pos]->update(delta, data)) {
|
||||
// updating might spawn/reallocate, cannot hold refs until here
|
||||
if(auto &curr = handlers.first()[pos]; curr->next) {
|
||||
curr = std::move(curr->next);
|
||||
// forces the process to exit the uninitialized state
|
||||
curr->update({}, nullptr);
|
||||
} else {
|
||||
curr = std::move(handlers.first().back());
|
||||
handlers.first().pop_back();
|
||||
}
|
||||
const auto pos = next - 1u;
|
||||
handlers.first()[pos]->tick(delta, data);
|
||||
// updating might spawn/reallocate, cannot hold refs until here
|
||||
auto &elem = handlers.first()[pos];
|
||||
|
||||
if(elem->finished()) {
|
||||
elem = elem->peek();
|
||||
}
|
||||
|
||||
if(!elem || elem->rejected()) {
|
||||
elem = std::move(handlers.first().back());
|
||||
handlers.first().pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -351,7 +210,11 @@ public:
|
||||
*/
|
||||
void abort(const bool immediate = false) {
|
||||
for(auto &&curr: handlers.first()) {
|
||||
curr->abort(immediate);
|
||||
curr->abort();
|
||||
|
||||
if(immediate) {
|
||||
curr->tick({});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ public:
|
||||
* @brief Returns the underlying resource handle.
|
||||
* @return The underlying resource handle.
|
||||
*/
|
||||
[[nodiscard]] const handle_type &handle() const noexcept {
|
||||
[[nodiscard]] handle_type handle() const noexcept {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ public:
|
||||
*/
|
||||
template<typename Type>
|
||||
void erase() {
|
||||
handlers.first().erase(type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value());
|
||||
handlers.first().erase(type_hash<std::remove_const_t<std::remove_reference_t<Type>>>::value());
|
||||
}
|
||||
|
||||
/*! @brief Disconnects all the listeners. */
|
||||
@@ -162,7 +162,7 @@ public:
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] bool contains() const {
|
||||
return handlers.first().contains(type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value());
|
||||
return handlers.first().contains(type_hash<std::remove_const_t<std::remove_reference_t<Type>>>::value());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// IWYU pragma: begin_exports
|
||||
#include "tools/davey.hpp"
|
||||
// IWYU pragma: end_exports
|
||||
@@ -0,0 +1,331 @@
|
||||
#ifndef ENTT_TOOLS_DAVEY_HPP
|
||||
#define ENTT_TOOLS_DAVEY_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <ios>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <imgui.h>
|
||||
#include "../entity/mixin.hpp"
|
||||
#include "../entity/registry.hpp"
|
||||
#include "../entity/sparse_set.hpp"
|
||||
#include "../entity/storage.hpp"
|
||||
#include "../locator/locator.hpp"
|
||||
#include "../meta/container.hpp"
|
||||
#include "../meta/context.hpp"
|
||||
#include "../meta/meta.hpp"
|
||||
#include "../meta/pointer.hpp"
|
||||
#include "../meta/resolve.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Entity, typename OnEntity>
|
||||
static void present_element(const meta_any &obj, OnEntity on_entity) {
|
||||
for([[maybe_unused]] const auto [id, data]: obj.type().data()) {
|
||||
const auto elem = data.get(obj);
|
||||
const char *label = data.name() ? data.name() : std::string{data.type().info().name()}.data();
|
||||
|
||||
if(auto type = data.type(); type.info() == type_id<const char *>()) {
|
||||
ImGui::Text("%s: %s", label, elem.template cast<const char *>());
|
||||
} else if(type.info() == type_id<std::string>()) {
|
||||
ImGui::Text("%s: %s", label, elem.template cast<const std::string &>().data());
|
||||
} else if(type.info() == type_id<Entity>()) {
|
||||
if(const auto entt = elem.template cast<Entity>(); entt == null) {
|
||||
ImGui::Text("%s: %s", label, "null");
|
||||
} else {
|
||||
on_entity(label, entt);
|
||||
}
|
||||
} else if(type.is_enum()) {
|
||||
const char *as_string = nullptr;
|
||||
|
||||
for(auto [id, curr]: type.data()) {
|
||||
if(curr.get({}) == elem) {
|
||||
as_string = curr.name();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(as_string) {
|
||||
ImGui::Text("%s: %s", label, as_string);
|
||||
} else {
|
||||
ImGui::Text("%s: %zu", label, elem.template allow_cast<std::uint64_t>().template cast<std::uint64_t>());
|
||||
}
|
||||
} else if(type.is_arithmetic()) {
|
||||
if(type.info() == type_id<bool>()) {
|
||||
std::stringstream buffer{};
|
||||
buffer << std::boolalpha << elem.template cast<bool>();
|
||||
ImGui::Text("%s: %s", label, buffer.str().data());
|
||||
} else if(type.info() == type_id<char>()) {
|
||||
ImGui::Text("%s: %c", label, elem.template cast<char>());
|
||||
} else if(type.is_integral()) {
|
||||
ImGui::Text("%s: %zu", label, elem.template allow_cast<std::uint64_t>().template cast<std::uint64_t>());
|
||||
} else {
|
||||
ImGui::Text("%s: %f", label, elem.template allow_cast<double>().template cast<double>());
|
||||
}
|
||||
} else if(type.is_pointer_like()) {
|
||||
if(auto deref = *obj; deref) {
|
||||
if(ImGui::TreeNode(label)) {
|
||||
present_element<Entity>(*obj, on_entity);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
} else {
|
||||
ImGui::Text("%s: %s", label, "null");
|
||||
}
|
||||
} else if(type.is_sequence_container()) {
|
||||
if(ImGui::TreeNode(label)) {
|
||||
meta_sequence_container view = elem.as_sequence_container();
|
||||
|
||||
for(std::size_t pos{}, last = view.size(); pos < last; ++pos) {
|
||||
ImGui::PushID(static_cast<int>(pos));
|
||||
|
||||
if(ImGui::TreeNode(label, "%zu", pos)) {
|
||||
present_element<Entity>(view[pos], on_entity);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
} else if(type.is_associative_container()) {
|
||||
if(ImGui::TreeNode(label)) {
|
||||
meta_associative_container view = elem.as_associative_container();
|
||||
auto it = view.begin();
|
||||
|
||||
for(std::size_t pos{}, last = view.size(); pos < last; ++pos, ++it) {
|
||||
ImGui::PushID(static_cast<int>(pos));
|
||||
|
||||
if(ImGui::TreeNode(label, "%zu", pos)) {
|
||||
const auto [key, value] = *it;
|
||||
|
||||
if(ImGui::TreeNode("key")) {
|
||||
present_element<Entity>(key, on_entity);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
if(ImGui::TreeNode("value")) {
|
||||
present_element<Entity>(value, on_entity);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
} else if(type.is_class()) {
|
||||
if(ImGui::TreeNode(label)) {
|
||||
present_element<Entity>(elem, on_entity);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
} else {
|
||||
const std::string underlying_type{data.type().info().name()};
|
||||
ImGui::Text("%s: %s", label, underlying_type.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Entity, typename Allocator>
|
||||
static void present_storage(const meta_ctx &ctx, const basic_sparse_set<Entity, Allocator> &storage) {
|
||||
if(auto type = resolve(ctx, storage.info()); type) {
|
||||
for(auto entt: storage) {
|
||||
ImGui::PushID(static_cast<int>(to_entity(entt)));
|
||||
|
||||
if(ImGui::TreeNode(&storage.info(), "%d [%d/%d]", to_integral(entt), to_entity(entt), to_version(entt))) {
|
||||
if(const auto obj = type.from_void(storage.value(entt)); obj) {
|
||||
present_element<typename std::decay_t<decltype(storage)>::entity_type>(obj, [](const char *name, const Entity entt) {
|
||||
ImGui::Text("%s: %d [%d/%d]", name, to_integral(entt), to_entity(entt), to_version(entt));
|
||||
});
|
||||
}
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
} else {
|
||||
for(auto entt: storage) {
|
||||
ImGui::Text("%d [%d/%d]", to_integral(entt), to_entity(entt), to_version(entt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Entity, typename It>
|
||||
static void present_entity(const meta_ctx &ctx, const Entity entt, const It from, const It to) {
|
||||
for(auto it = from; it != to; ++it) {
|
||||
if(const auto &storage = it->second; storage.contains(entt)) {
|
||||
if(auto type = resolve(ctx, storage.info()); type) {
|
||||
const char *label = type.name() ? type.name() : std::string{storage.info().name()}.data();
|
||||
|
||||
if(ImGui::TreeNode(&storage.info(), "%s", label)) {
|
||||
if(const auto obj = type.from_void(storage.value(entt)); obj) {
|
||||
present_element<Entity>(obj, [&ctx, from, to](const char *name, const Entity other) {
|
||||
if(ImGui::TreeNode(name, "%s: %d [%d/%d]", name, to_integral(other), to_entity(other), to_version(other))) {
|
||||
present_entity<Entity>(ctx, other, from, to);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
} else {
|
||||
const std::string name{storage.info().name()};
|
||||
ImGui::Text("%s", name.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Get, typename... Exclude, std::size_t... Index>
|
||||
static void present_view(const meta_ctx &ctx, const basic_view<get_t<Get...>, exclude_t<Exclude...>> &view, std::index_sequence<Index...>) {
|
||||
using view_type = basic_view<get_t<Get...>, exclude_t<Exclude...>>;
|
||||
const std::array<const typename view_type::common_type *, sizeof...(Index)> range{view.template storage<Index>()...};
|
||||
|
||||
for(auto tup: view.each()) {
|
||||
const auto entt = std::get<0>(tup);
|
||||
ImGui::PushID(static_cast<int>(to_entity(entt)));
|
||||
|
||||
if(ImGui::TreeNode(&type_id<typename view_type::entity_type>(), "%d [%d/%d]", to_integral(entt), to_entity(entt), to_version(entt))) {
|
||||
for(const auto *storage: range) {
|
||||
if(auto type = resolve(ctx, storage->info()); type) {
|
||||
const char *label = type.name() ? type.name() : std::string{storage->info().name()}.data();
|
||||
|
||||
if(ImGui::TreeNode(&storage->info(), "%s", label)) {
|
||||
if(const auto obj = type.from_void(storage->value(entt)); obj) {
|
||||
present_element<typename view_type::entity_type>(obj, [](const char *name, const typename view_type::entity_type entt) {
|
||||
ImGui::Text("%s: %d [%d/%d]", name, to_integral(entt), to_entity(entt), to_version(entt));
|
||||
});
|
||||
}
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
} else {
|
||||
const std::string name{storage->info().name()};
|
||||
ImGui::Text("%s", name.data());
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief ImGui-based introspection tool for storage types.
|
||||
* @tparam Type Storage element type.
|
||||
* @tparam Entity Storage entity type.
|
||||
* @tparam Allocator Storage allocator type.
|
||||
* @param ctx The context from which to search for meta types.
|
||||
* @param storage An instance of the storage type.
|
||||
*/
|
||||
template<typename Type, typename Entity, typename Allocator>
|
||||
void davey(const meta_ctx &ctx, const basic_storage<Type, Entity, Allocator> &storage) {
|
||||
internal::present_storage(ctx, storage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ImGui-based introspection tool for storage types.
|
||||
* @tparam Type Storage element type.
|
||||
* @tparam Entity Storage entity type.
|
||||
* @tparam Allocator Storage allocator type.
|
||||
* @param storage An instance of the storage type.
|
||||
*/
|
||||
template<typename Type, typename Entity, typename Allocator>
|
||||
void davey(const basic_storage<Type, Entity, Allocator> &storage) {
|
||||
davey(locator<meta_ctx>::value_or(), storage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ImGui-based introspection tool for view types.
|
||||
* @tparam Get Types of storage iterated by the view.
|
||||
* @tparam Exclude Types of storage used to filter the view.
|
||||
* @param ctx The context from which to search for meta types.
|
||||
* @param view An instance of the view type.
|
||||
*/
|
||||
template<typename... Get, typename... Exclude>
|
||||
void davey(const meta_ctx &ctx, const basic_view<get_t<Get...>, exclude_t<Exclude...>> &view) {
|
||||
internal::present_view(ctx, view, std::index_sequence_for<Get...>{});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ImGui-based introspection tool for view types.
|
||||
* @tparam Get Types of storage iterated by the view.
|
||||
* @tparam Exclude Types of storage used to filter the view.
|
||||
* @param view An instance of the view type.
|
||||
*/
|
||||
template<typename... Get, typename... Exclude>
|
||||
void davey(const basic_view<get_t<Get...>, exclude_t<Exclude...>> &view) {
|
||||
davey(locator<meta_ctx>::value_or(), view);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ImGui-based introspection tool for registry types.
|
||||
* @tparam Entity Registry entity type.
|
||||
* @tparam Allocator Registry allocator type.
|
||||
* @param ctx The context from which to search for meta types.
|
||||
* @param registry An instance of the registry type.
|
||||
*/
|
||||
template<typename Entity, typename Allocator>
|
||||
void davey(const meta_ctx &ctx, const basic_registry<Entity, Allocator> ®istry) {
|
||||
ImGui::BeginTabBar("#tabs");
|
||||
|
||||
if(ImGui::BeginTabItem("Entity")) {
|
||||
for(const auto [entt]: registry.template storage<Entity>()->each()) {
|
||||
ImGui::PushID(static_cast<int>(to_entity(entt)));
|
||||
|
||||
if(ImGui::TreeNode(&type_id<Entity>(), "%d [%d/%d]", to_integral(entt), to_entity(entt), to_version(entt))) {
|
||||
const auto range = registry.storage();
|
||||
internal::present_entity(ctx, entt, range.begin(), range.end());
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if(ImGui::BeginTabItem("Storage")) {
|
||||
for([[maybe_unused]] auto [id, storage]: registry.storage()) {
|
||||
const auto type = resolve(ctx, storage.info());
|
||||
const char *label = type.name() ? type.name() : std::string{storage.info().name()}.data();
|
||||
|
||||
if(ImGui::TreeNode(&storage.info(), "%s (%zu)", label, storage.size())) {
|
||||
internal::present_storage(ctx, storage);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ImGui-based introspection tool for registry types.
|
||||
* @tparam Entity Registry entity type.
|
||||
* @tparam Allocator Registry allocator type.
|
||||
* @param registry An instance of the registry type.
|
||||
*/
|
||||
template<typename Entity, typename Allocator>
|
||||
void davey(const basic_registry<Entity, Allocator> ®istry) {
|
||||
davey(locator<meta_ctx>::value_or(), registry);
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
+7
-9
@@ -43,6 +43,8 @@ function(SETUP_TARGET TARGET_NAME)
|
||||
target_link_libraries(${TARGET_NAME} PRIVATE EnTT)
|
||||
|
||||
if(MSVC)
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/PROFILE")
|
||||
|
||||
target_compile_options(
|
||||
${TARGET_NAME}
|
||||
PRIVATE
|
||||
@@ -63,6 +65,7 @@ function(SETUP_TARGET TARGET_NAME)
|
||||
PRIVATE
|
||||
# disabling INCREMENTAL is required by SizeBench
|
||||
$<$<CONFIG:Debug>:/INCREMENTAL:NO>
|
||||
$<$<CONFIG:Release>:/OPT:NOICF>
|
||||
)
|
||||
else()
|
||||
target_compile_options(
|
||||
@@ -115,13 +118,12 @@ function(SETUP_LIB_PLUGIN_TEST TEST_NAME SUB_PATH)
|
||||
add_library(_${TARGET_NAME} MODULE $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/${SUB_PATH}/plugin.cpp)
|
||||
SETUP_TARGET(_${TARGET_NAME} ${ARGVN})
|
||||
SETUP_BASIC_TEST(lib_${TARGET_NAME} lib/${TEST_NAME}/${SUB_PATH}/main.cpp PLUGIN="$<TARGET_FILE:_${TARGET_NAME}>" ${ARGVN})
|
||||
target_link_libraries(_${TARGET_NAME} PRIVATE cr::cr)
|
||||
target_link_libraries(lib_${TARGET_NAME} PRIVATE cr::cr ${CMAKE_DL_LIBS})
|
||||
set_target_properties(_${TARGET_NAME} PROPERTIES CXX_CLANG_TIDY "")
|
||||
set_target_properties(lib_${TARGET_NAME} PROPERTIES CXX_CLANG_TIDY "")
|
||||
target_include_directories(_${TARGET_NAME} PRIVATE ${cr_INCLUDE_DIR})
|
||||
target_include_directories(lib_${TARGET_NAME} PRIVATE ${cr_INCLUDE_DIR})
|
||||
target_compile_options(_${TARGET_NAME} PRIVATE $<$<NOT:$<STREQUAL:"${CMAKE_CXX_COMPILER_ID}","MSVC">>:-Wno-conversion>)
|
||||
target_compile_options(lib_${TARGET_NAME} PRIVATE $<$<NOT:$<STREQUAL:"${CMAKE_CXX_COMPILER_ID}","MSVC">>:-Wno-conversion>)
|
||||
target_link_libraries(lib_${TARGET_NAME} PRIVATE ${CMAKE_DL_LIBS})
|
||||
add_dependencies(lib_${TARGET_NAME} _${TARGET_NAME})
|
||||
endfunction()
|
||||
|
||||
@@ -151,12 +153,9 @@ if(ENTT_BUILD_LIB)
|
||||
GIT_SHALLOW 1
|
||||
)
|
||||
|
||||
FetchContent_GetProperties(cr)
|
||||
FetchContent_MakeAvailable(cr)
|
||||
|
||||
if(NOT cr_POPULATED)
|
||||
FetchContent_Populate(cr)
|
||||
set(cr_INCLUDE_DIR ${cr_SOURCE_DIR})
|
||||
endif()
|
||||
add_library(cr::cr ALIAS cr)
|
||||
|
||||
SETUP_LIB_SHARED_TEST(dispatcher shared)
|
||||
SETUP_LIB_PLUGIN_TEST(dispatcher plugin)
|
||||
@@ -271,7 +270,6 @@ SETUP_BASIC_TEST(meta_conv entt/meta/meta_conv.cpp)
|
||||
SETUP_BASIC_TEST(meta_ctor entt/meta/meta_ctor.cpp)
|
||||
SETUP_BASIC_TEST(meta_custom entt/meta/meta_custom.cpp)
|
||||
SETUP_BASIC_TEST(meta_data entt/meta/meta_data.cpp)
|
||||
SETUP_BASIC_TEST(meta_dtor entt/meta/meta_dtor.cpp)
|
||||
SETUP_BASIC_TEST(meta_factory entt/meta/meta_factory.cpp)
|
||||
SETUP_BASIC_TEST(meta_func entt/meta/meta_func.cpp)
|
||||
SETUP_BASIC_TEST(meta_handle entt/meta/meta_handle.cpp)
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
|
||||
namespace test {
|
||||
|
||||
enum class meta_traits {
|
||||
none = 0x0000,
|
||||
one = 0x0001,
|
||||
two = 0x0002,
|
||||
three = 0x0100,
|
||||
_entt_enum_as_bitmask
|
||||
enum class meta_traits : std::uint8_t {
|
||||
none = 0x00,
|
||||
one = 0x01,
|
||||
two = 0x02,
|
||||
three = 0x04,
|
||||
all = 0xFF,
|
||||
_entt_enum_as_bitmask = all
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
@@ -818,8 +819,8 @@ TEST(DenseMap, EraseFromBucket) {
|
||||
|
||||
for(std::size_t next{}; next < 4u; ++next) {
|
||||
ASSERT_TRUE(map.emplace(2u * minimum_bucket_count * next, 2u * minimum_bucket_count * next).second);
|
||||
ASSERT_TRUE(map.emplace(2u * minimum_bucket_count * next + 2u, 2u * minimum_bucket_count * next + 2u).second);
|
||||
ASSERT_TRUE(map.emplace(2u * minimum_bucket_count * next + 3u, 2u * minimum_bucket_count * next + 3u).second);
|
||||
ASSERT_TRUE(map.emplace((2u * minimum_bucket_count * next) + 2u, (2u * minimum_bucket_count * next) + 2u).second);
|
||||
ASSERT_TRUE(map.emplace((2u * minimum_bucket_count * next) + 3u, (2u * minimum_bucket_count * next) + 3u).second);
|
||||
}
|
||||
|
||||
ASSERT_EQ(map.bucket_count(), 2u * minimum_bucket_count);
|
||||
@@ -842,16 +843,16 @@ TEST(DenseMap, EraseFromBucket) {
|
||||
ASSERT_TRUE(map.contains(2u * minimum_bucket_count * next));
|
||||
ASSERT_EQ(map.bucket(2u * minimum_bucket_count * next), 0u);
|
||||
|
||||
ASSERT_TRUE(map.contains(2u * minimum_bucket_count * next + 2u));
|
||||
ASSERT_EQ(map.bucket(2u * minimum_bucket_count * next + 2u), 2u);
|
||||
ASSERT_TRUE(map.contains((2u * minimum_bucket_count * next) + 2u));
|
||||
ASSERT_EQ(map.bucket((2u * minimum_bucket_count * next) + 2u), 2u);
|
||||
|
||||
ASSERT_TRUE(map.contains(2u * minimum_bucket_count * next + 3u));
|
||||
ASSERT_EQ(map.bucket(2u * minimum_bucket_count * next + 3u), 3u);
|
||||
ASSERT_TRUE(map.contains((2u * minimum_bucket_count * next) + 3u));
|
||||
ASSERT_EQ(map.bucket((2u * minimum_bucket_count * next) + 3u), 3u);
|
||||
}
|
||||
|
||||
ASSERT_FALSE(map.contains(2u * minimum_bucket_count * 3u));
|
||||
ASSERT_FALSE(map.contains(2u * minimum_bucket_count * 3u + 2u));
|
||||
ASSERT_FALSE(map.contains(2u * minimum_bucket_count * 3u + 3u));
|
||||
ASSERT_FALSE(map.contains((2u * minimum_bucket_count * 3u) + 2u));
|
||||
ASSERT_FALSE(map.contains((2u * minimum_bucket_count * 3u) + 3u));
|
||||
|
||||
map.erase((++map.begin(0u))->first);
|
||||
map.erase((++map.begin(2u))->first);
|
||||
@@ -865,8 +866,8 @@ TEST(DenseMap, EraseFromBucket) {
|
||||
ASSERT_EQ(map.bucket_size(3u), 2u);
|
||||
|
||||
ASSERT_FALSE(map.contains(2u * minimum_bucket_count * 1u));
|
||||
ASSERT_FALSE(map.contains(2u * minimum_bucket_count * 1u + 2u));
|
||||
ASSERT_FALSE(map.contains(2u * minimum_bucket_count * 1u + 3u));
|
||||
ASSERT_FALSE(map.contains((2u * minimum_bucket_count * 1u) + 2u));
|
||||
ASSERT_FALSE(map.contains((2u * minimum_bucket_count * 1u) + 3u));
|
||||
|
||||
while(map.begin(3) != map.end(3u)) {
|
||||
map.erase(map.begin(3)->first);
|
||||
@@ -880,11 +881,11 @@ TEST(DenseMap, EraseFromBucket) {
|
||||
ASSERT_EQ(map.bucket_size(3u), 0u);
|
||||
|
||||
ASSERT_TRUE(map.contains(0u * minimum_bucket_count));
|
||||
ASSERT_TRUE(map.contains(0u * minimum_bucket_count + 2u));
|
||||
ASSERT_TRUE(map.contains((0u * minimum_bucket_count) + 2u));
|
||||
ASSERT_TRUE(map.contains(4u * minimum_bucket_count));
|
||||
ASSERT_TRUE(map.contains(4u * minimum_bucket_count + 2u));
|
||||
ASSERT_TRUE(map.contains((4u * minimum_bucket_count) + 2u));
|
||||
|
||||
map.erase(4u * minimum_bucket_count + 2u);
|
||||
map.erase((4u * minimum_bucket_count) + 2u);
|
||||
map.erase(0u * minimum_bucket_count);
|
||||
|
||||
ASSERT_EQ(map.bucket_count(), 2u * minimum_bucket_count);
|
||||
@@ -895,9 +896,9 @@ TEST(DenseMap, EraseFromBucket) {
|
||||
ASSERT_EQ(map.bucket_size(3u), 0u);
|
||||
|
||||
ASSERT_FALSE(map.contains(0u * minimum_bucket_count));
|
||||
ASSERT_TRUE(map.contains(0u * minimum_bucket_count + 2u));
|
||||
ASSERT_TRUE(map.contains((0u * minimum_bucket_count) + 2u));
|
||||
ASSERT_TRUE(map.contains(4u * minimum_bucket_count));
|
||||
ASSERT_FALSE(map.contains(4u * minimum_bucket_count + 2u));
|
||||
ASSERT_FALSE(map.contains((4u * minimum_bucket_count) + 2u));
|
||||
}
|
||||
|
||||
TEST(DenseMap, Swap) {
|
||||
@@ -959,7 +960,7 @@ TEST(DenseMap, EqualRange) {
|
||||
}
|
||||
|
||||
TEST(DenseMap, Indexing) {
|
||||
entt::dense_map<int, int> map;
|
||||
entt::dense_map<int, int, entt::identity, test::transparent_equal_to> map;
|
||||
const auto &cmap = map;
|
||||
const auto key = 1;
|
||||
|
||||
@@ -969,16 +970,23 @@ TEST(DenseMap, Indexing) {
|
||||
|
||||
ASSERT_TRUE(map.contains(key));
|
||||
ASSERT_EQ(map[int{key}], 3);
|
||||
ASSERT_EQ(cmap.at(key), 3);
|
||||
|
||||
ASSERT_EQ(map.at(key), 3);
|
||||
ASSERT_EQ(cmap.at(key), 3);
|
||||
|
||||
ASSERT_EQ(map.at(static_cast<double>(key)), 3);
|
||||
ASSERT_EQ(cmap.at(static_cast<double>(key)), 3);
|
||||
}
|
||||
|
||||
ENTT_DEBUG_TEST(DenseMapDeathTest, Indexing) {
|
||||
entt::dense_map<int, int> map;
|
||||
entt::dense_map<int, int, entt::identity, test::transparent_equal_to> map;
|
||||
const auto &cmap = map;
|
||||
|
||||
ASSERT_DEATH([[maybe_unused]] auto value = cmap.at(0), "");
|
||||
ASSERT_DEATH([[maybe_unused]] auto value = map.at(3), "");
|
||||
ASSERT_DEATH([[maybe_unused]] auto value = cmap.at(0), "");
|
||||
|
||||
ASSERT_DEATH([[maybe_unused]] auto value = map.at(3.), "");
|
||||
ASSERT_DEATH([[maybe_unused]] auto value = cmap.at(0.), "");
|
||||
}
|
||||
|
||||
TEST(DenseMap, LocalIterator) {
|
||||
@@ -1073,7 +1081,7 @@ TEST(DenseMap, Rehash) {
|
||||
ASSERT_EQ(map.bucket(32u), 0u);
|
||||
ASSERT_EQ(map[32u], 2u);
|
||||
|
||||
map.rehash(4u * minimum_bucket_count + 1u);
|
||||
map.rehash((4u * minimum_bucket_count) + 1u);
|
||||
|
||||
ASSERT_EQ(map.bucket_count(), 8u * minimum_bucket_count);
|
||||
ASSERT_TRUE(map.contains(32u));
|
||||
@@ -1099,7 +1107,7 @@ TEST(DenseMap, Rehash) {
|
||||
ASSERT_EQ(map.bucket_count(), 2u * minimum_bucket_count);
|
||||
ASSERT_TRUE(map.contains(32u));
|
||||
|
||||
map.rehash(4u * minimum_bucket_count + 4u);
|
||||
map.rehash((4u * minimum_bucket_count) + 4u);
|
||||
|
||||
ASSERT_EQ(map.bucket_count(), 8u * minimum_bucket_count);
|
||||
ASSERT_TRUE(map.contains(32u));
|
||||
|
||||
@@ -676,8 +676,8 @@ TEST(DenseSet, EraseFromBucket) {
|
||||
|
||||
for(std::size_t next{}; next < 4u; ++next) {
|
||||
ASSERT_TRUE(set.emplace(2u * minimum_bucket_count * next).second);
|
||||
ASSERT_TRUE(set.emplace(2u * minimum_bucket_count * next + 2u).second);
|
||||
ASSERT_TRUE(set.emplace(2u * minimum_bucket_count * next + 3u).second);
|
||||
ASSERT_TRUE(set.emplace((2u * minimum_bucket_count * next) + 2u).second);
|
||||
ASSERT_TRUE(set.emplace((2u * minimum_bucket_count * next) + 3u).second);
|
||||
}
|
||||
|
||||
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
|
||||
@@ -700,16 +700,16 @@ TEST(DenseSet, EraseFromBucket) {
|
||||
ASSERT_TRUE(set.contains(2u * minimum_bucket_count * next));
|
||||
ASSERT_EQ(set.bucket(2u * minimum_bucket_count * next), 0u);
|
||||
|
||||
ASSERT_TRUE(set.contains(2u * minimum_bucket_count * next + 2u));
|
||||
ASSERT_EQ(set.bucket(2u * minimum_bucket_count * next + 2u), 2u);
|
||||
ASSERT_TRUE(set.contains((2u * minimum_bucket_count * next) + 2u));
|
||||
ASSERT_EQ(set.bucket((2u * minimum_bucket_count * next) + 2u), 2u);
|
||||
|
||||
ASSERT_TRUE(set.contains(2u * minimum_bucket_count * next + 3u));
|
||||
ASSERT_EQ(set.bucket(2u * minimum_bucket_count * next + 3u), 3u);
|
||||
ASSERT_TRUE(set.contains((2u * minimum_bucket_count * next) + 3u));
|
||||
ASSERT_EQ(set.bucket((2u * minimum_bucket_count * next) + 3u), 3u);
|
||||
}
|
||||
|
||||
ASSERT_FALSE(set.contains(2u * minimum_bucket_count * 3u));
|
||||
ASSERT_FALSE(set.contains(2u * minimum_bucket_count * 3u + 2u));
|
||||
ASSERT_FALSE(set.contains(2u * minimum_bucket_count * 3u + 3u));
|
||||
ASSERT_FALSE(set.contains((2u * minimum_bucket_count * 3u) + 2u));
|
||||
ASSERT_FALSE(set.contains((2u * minimum_bucket_count * 3u) + 3u));
|
||||
|
||||
set.erase(*++set.begin(0u));
|
||||
set.erase(*++set.begin(2u));
|
||||
@@ -723,8 +723,8 @@ TEST(DenseSet, EraseFromBucket) {
|
||||
ASSERT_EQ(set.bucket_size(3u), 2u);
|
||||
|
||||
ASSERT_FALSE(set.contains(2u * minimum_bucket_count * 1u));
|
||||
ASSERT_FALSE(set.contains(2u * minimum_bucket_count * 1u + 2u));
|
||||
ASSERT_FALSE(set.contains(2u * minimum_bucket_count * 1u + 3u));
|
||||
ASSERT_FALSE(set.contains((2u * minimum_bucket_count * 1u) + 2u));
|
||||
ASSERT_FALSE(set.contains((2u * minimum_bucket_count * 1u) + 3u));
|
||||
|
||||
while(set.begin(3) != set.end(3u)) {
|
||||
set.erase(*set.begin(3));
|
||||
@@ -738,11 +738,11 @@ TEST(DenseSet, EraseFromBucket) {
|
||||
ASSERT_EQ(set.bucket_size(3u), 0u);
|
||||
|
||||
ASSERT_TRUE(set.contains(0u * minimum_bucket_count));
|
||||
ASSERT_TRUE(set.contains(0u * minimum_bucket_count + 2u));
|
||||
ASSERT_TRUE(set.contains((0u * minimum_bucket_count) + 2u));
|
||||
ASSERT_TRUE(set.contains(4u * minimum_bucket_count));
|
||||
ASSERT_TRUE(set.contains(4u * minimum_bucket_count + 2u));
|
||||
ASSERT_TRUE(set.contains((4u * minimum_bucket_count) + 2u));
|
||||
|
||||
set.erase(4u * minimum_bucket_count + 2u);
|
||||
set.erase((4u * minimum_bucket_count) + 2u);
|
||||
set.erase(0u * minimum_bucket_count);
|
||||
|
||||
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
|
||||
@@ -753,9 +753,9 @@ TEST(DenseSet, EraseFromBucket) {
|
||||
ASSERT_EQ(set.bucket_size(3u), 0u);
|
||||
|
||||
ASSERT_FALSE(set.contains(0u * minimum_bucket_count));
|
||||
ASSERT_TRUE(set.contains(0u * minimum_bucket_count + 2u));
|
||||
ASSERT_TRUE(set.contains((0u * minimum_bucket_count) + 2u));
|
||||
ASSERT_TRUE(set.contains(4u * minimum_bucket_count));
|
||||
ASSERT_FALSE(set.contains(4u * minimum_bucket_count + 2u));
|
||||
ASSERT_FALSE(set.contains((4u * minimum_bucket_count) + 2u));
|
||||
}
|
||||
|
||||
TEST(DenseSet, Swap) {
|
||||
@@ -902,7 +902,7 @@ TEST(DenseSet, Rehash) {
|
||||
ASSERT_TRUE(set.contains(32u));
|
||||
ASSERT_EQ(set.bucket(32u), 0u);
|
||||
|
||||
set.rehash(4u * minimum_bucket_count + 1u);
|
||||
set.rehash((4u * minimum_bucket_count) + 1u);
|
||||
|
||||
ASSERT_EQ(set.bucket_count(), 8u * minimum_bucket_count);
|
||||
ASSERT_TRUE(set.contains(32u));
|
||||
@@ -926,7 +926,7 @@ TEST(DenseSet, Rehash) {
|
||||
ASSERT_EQ(set.bucket_count(), 2u * minimum_bucket_count);
|
||||
ASSERT_TRUE(set.contains(32u));
|
||||
|
||||
set.rehash(4u * minimum_bucket_count + 4u);
|
||||
set.rehash((4u * minimum_bucket_count) + 4u);
|
||||
|
||||
ASSERT_EQ(set.bucket_count(), 8u * minimum_bucket_count);
|
||||
ASSERT_TRUE(set.contains(32u));
|
||||
|
||||
+157
-100
@@ -57,6 +57,24 @@ TEST(Any, Empty) {
|
||||
ASSERT_EQ(any.data(), nullptr);
|
||||
}
|
||||
|
||||
TEST(Any, HasValue) {
|
||||
entt::any any{};
|
||||
|
||||
ASSERT_FALSE(any.has_value());
|
||||
ASSERT_FALSE(any.has_value(entt::type_id<char>()));
|
||||
ASSERT_FALSE(any.has_value(entt::type_id<int>()));
|
||||
ASSERT_FALSE(any.has_value<char>());
|
||||
ASSERT_FALSE(any.has_value<int>());
|
||||
|
||||
any = 2;
|
||||
|
||||
ASSERT_TRUE(any.has_value());
|
||||
ASSERT_FALSE(any.has_value(entt::type_id<char>()));
|
||||
ASSERT_TRUE(any.has_value(entt::type_id<int>()));
|
||||
ASSERT_FALSE(any.has_value<char>());
|
||||
ASSERT_TRUE(any.has_value<int>());
|
||||
}
|
||||
|
||||
TEST(Any, SBO) {
|
||||
entt::any any{'c'};
|
||||
|
||||
@@ -88,7 +106,7 @@ TEST(Any, SBOInPlaceConstruction) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::dynamic);
|
||||
ASSERT_EQ(any.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(any), 2);
|
||||
|
||||
@@ -97,7 +115,7 @@ TEST(Any, SBOInPlaceConstruction) {
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_FALSE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::ref);
|
||||
ASSERT_EQ(other.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<int>(other), 2);
|
||||
ASSERT_EQ(other.data(), any.data());
|
||||
}
|
||||
@@ -109,7 +127,7 @@ TEST(Any, SBOInPlaceNullptrConstruction) {
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(any.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<void>());
|
||||
}
|
||||
|
||||
TEST(Any, SBOInPlaceTypeConstruction) {
|
||||
@@ -118,7 +136,7 @@ TEST(Any, SBOInPlaceTypeConstruction) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::embedded);
|
||||
ASSERT_EQ(any.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(any), 2);
|
||||
|
||||
@@ -127,7 +145,7 @@ TEST(Any, SBOInPlaceTypeConstruction) {
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_FALSE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::ref);
|
||||
ASSERT_EQ(other.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<int>(other), 2);
|
||||
ASSERT_EQ(other.data(), any.data());
|
||||
}
|
||||
@@ -139,7 +157,7 @@ TEST(Any, SBOAsRefConstruction) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::ref);
|
||||
ASSERT_EQ(any.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<int>());
|
||||
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<const int>(&any), &value);
|
||||
@@ -158,7 +176,7 @@ TEST(Any, SBOAsRefConstruction) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::ref);
|
||||
ASSERT_EQ(any.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<int>(&any), &value);
|
||||
|
||||
auto other = any.as_ref();
|
||||
@@ -166,7 +184,7 @@ TEST(Any, SBOAsRefConstruction) {
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_FALSE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::ref);
|
||||
ASSERT_EQ(other.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<int>(other), 2);
|
||||
ASSERT_EQ(other.data(), any.data());
|
||||
}
|
||||
@@ -178,7 +196,7 @@ TEST(Any, SBOAsConstRefConstruction) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::cref);
|
||||
ASSERT_EQ(any.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<int>());
|
||||
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<const int>(&any), &value);
|
||||
@@ -197,7 +215,7 @@ TEST(Any, SBOAsConstRefConstruction) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::cref);
|
||||
ASSERT_EQ(any.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<const int>(&any), &value);
|
||||
|
||||
auto other = any.as_ref();
|
||||
@@ -205,7 +223,7 @@ TEST(Any, SBOAsConstRefConstruction) {
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_FALSE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::cref);
|
||||
ASSERT_EQ(other.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<int>(other), 2);
|
||||
ASSERT_EQ(other.data(), any.data());
|
||||
}
|
||||
@@ -218,7 +236,7 @@ TEST(Any, SBOCopyConstruction) {
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_TRUE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::embedded);
|
||||
ASSERT_EQ(other.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(other), 2);
|
||||
}
|
||||
@@ -233,7 +251,7 @@ TEST(Any, SBOCopyAssignment) {
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_TRUE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::embedded);
|
||||
ASSERT_EQ(other.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(other), 2);
|
||||
}
|
||||
@@ -247,7 +265,7 @@ TEST(Any, SBOSelfCopyAssignment) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::embedded);
|
||||
ASSERT_EQ(any.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(any), 2);
|
||||
}
|
||||
@@ -262,7 +280,7 @@ TEST(Any, SBOMoveConstruction) {
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_TRUE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::embedded);
|
||||
ASSERT_EQ(other.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(other), 2);
|
||||
}
|
||||
@@ -278,22 +296,22 @@ TEST(Any, SBOMoveAssignment) {
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_TRUE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::embedded);
|
||||
ASSERT_EQ(other.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(other), 2);
|
||||
}
|
||||
|
||||
TEST(AnyDeathTest, SBOSelfMoveAssignment) {
|
||||
TEST(Any, SBOSelfMoveAssignment) {
|
||||
entt::any any{2};
|
||||
|
||||
// avoid warnings due to self-assignment
|
||||
any = std::move(*&any);
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(any.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(any.data(), nullptr);
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::embedded);
|
||||
ASSERT_EQ(any.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<int>(any), 2);
|
||||
}
|
||||
|
||||
TEST(Any, SBODirectAssignment) {
|
||||
@@ -303,7 +321,7 @@ TEST(Any, SBODirectAssignment) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::embedded);
|
||||
ASSERT_EQ(any.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(any), 2);
|
||||
}
|
||||
@@ -407,7 +425,7 @@ TEST(Any, NoSBOInPlaceConstruction) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::dynamic);
|
||||
ASSERT_EQ(any.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(any), (fat{.1, .2, .3, .4}));
|
||||
|
||||
@@ -416,7 +434,7 @@ TEST(Any, NoSBOInPlaceConstruction) {
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_FALSE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::ref);
|
||||
ASSERT_EQ(other.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<fat>(other), (fat{.1, .2, .3, .4}));
|
||||
ASSERT_EQ(other.data(), any.data());
|
||||
}
|
||||
@@ -428,7 +446,7 @@ TEST(Any, NoSBOInPlaceNullptrConstruction) {
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(any.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<void>());
|
||||
}
|
||||
|
||||
TEST(Any, NoSBOInPlaceTypeConstruction) {
|
||||
@@ -438,7 +456,7 @@ TEST(Any, NoSBOInPlaceTypeConstruction) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::dynamic);
|
||||
ASSERT_EQ(any.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(any), instance);
|
||||
|
||||
@@ -447,7 +465,7 @@ TEST(Any, NoSBOInPlaceTypeConstruction) {
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_FALSE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::ref);
|
||||
ASSERT_EQ(other.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<fat>(other), (fat{.1, .2, .3, .4}));
|
||||
ASSERT_EQ(other.data(), any.data());
|
||||
}
|
||||
@@ -459,7 +477,7 @@ TEST(Any, NoSBOAsRefConstruction) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::ref);
|
||||
ASSERT_EQ(any.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<fat>());
|
||||
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<const fat>(&any), &instance);
|
||||
@@ -478,7 +496,7 @@ TEST(Any, NoSBOAsRefConstruction) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::ref);
|
||||
ASSERT_EQ(any.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<fat>(&any), &instance);
|
||||
|
||||
auto other = any.as_ref();
|
||||
@@ -486,7 +504,7 @@ TEST(Any, NoSBOAsRefConstruction) {
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_FALSE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::ref);
|
||||
ASSERT_EQ(other.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<fat>(other), (fat{.1, .2, .3, .4}));
|
||||
ASSERT_EQ(other.data(), any.data());
|
||||
}
|
||||
@@ -498,7 +516,7 @@ TEST(Any, NoSBOAsConstRefConstruction) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::cref);
|
||||
ASSERT_EQ(any.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<fat>());
|
||||
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<const fat>(&any), &instance);
|
||||
@@ -517,7 +535,7 @@ TEST(Any, NoSBOAsConstRefConstruction) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::cref);
|
||||
ASSERT_EQ(any.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<const fat>(&any), &instance);
|
||||
|
||||
auto other = any.as_ref();
|
||||
@@ -525,7 +543,7 @@ TEST(Any, NoSBOAsConstRefConstruction) {
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_FALSE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::cref);
|
||||
ASSERT_EQ(other.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<fat>(other), (fat{.1, .2, .3, .4}));
|
||||
ASSERT_EQ(other.data(), any.data());
|
||||
}
|
||||
@@ -539,7 +557,7 @@ TEST(Any, NoSBOCopyConstruction) {
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_TRUE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::dynamic);
|
||||
ASSERT_EQ(other.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(other), instance);
|
||||
}
|
||||
@@ -555,7 +573,7 @@ TEST(Any, NoSBOCopyAssignment) {
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_TRUE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::dynamic);
|
||||
ASSERT_EQ(other.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(other), instance);
|
||||
}
|
||||
@@ -570,7 +588,7 @@ TEST(Any, NoSBOSelfCopyAssignment) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::dynamic);
|
||||
ASSERT_EQ(any.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(any), instance);
|
||||
}
|
||||
@@ -586,7 +604,7 @@ TEST(Any, NoSBOMoveConstruction) {
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_TRUE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::dynamic);
|
||||
ASSERT_EQ(other.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(other), instance);
|
||||
}
|
||||
@@ -603,23 +621,23 @@ TEST(Any, NoSBOMoveAssignment) {
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_TRUE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::dynamic);
|
||||
ASSERT_EQ(other.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(other), instance);
|
||||
}
|
||||
|
||||
TEST(AnyDeathTest, NoSBOSelfMoveAssignment) {
|
||||
TEST(Any, NoSBOSelfMoveAssignment) {
|
||||
const fat instance{.1, .2, .3, .4};
|
||||
entt::any any{instance};
|
||||
|
||||
// avoid warnings due to self-assignment
|
||||
any = std::move(*&any);
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(any.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(any.data(), nullptr);
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::dynamic);
|
||||
ASSERT_EQ(any.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<fat>(any), instance);
|
||||
}
|
||||
|
||||
TEST(Any, NoSBODirectAssignment) {
|
||||
@@ -630,7 +648,7 @@ TEST(Any, NoSBODirectAssignment) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::dynamic);
|
||||
ASSERT_EQ(any.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(any), instance);
|
||||
}
|
||||
@@ -747,7 +765,7 @@ TEST(Any, VoidInPlaceTypeConstruction) {
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(any.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<void>());
|
||||
ASSERT_EQ(entt::any_cast<int>(&any), nullptr);
|
||||
}
|
||||
|
||||
@@ -759,7 +777,7 @@ TEST(Any, VoidCopyConstruction) {
|
||||
ASSERT_FALSE(other);
|
||||
ASSERT_FALSE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(other.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<void>());
|
||||
ASSERT_EQ(entt::any_cast<int>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
}
|
||||
@@ -774,7 +792,7 @@ TEST(Any, VoidCopyAssignment) {
|
||||
ASSERT_FALSE(other);
|
||||
ASSERT_FALSE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(other.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<void>());
|
||||
ASSERT_EQ(entt::any_cast<int>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
}
|
||||
@@ -788,7 +806,7 @@ TEST(Any, VoidSelfCopyAssignment) {
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(any.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<void>());
|
||||
ASSERT_EQ(entt::any_cast<int>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
}
|
||||
@@ -803,7 +821,7 @@ TEST(Any, VoidMoveConstruction) {
|
||||
ASSERT_FALSE(other);
|
||||
ASSERT_FALSE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(other.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<void>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
}
|
||||
|
||||
@@ -818,11 +836,11 @@ TEST(Any, VoidMoveAssignment) {
|
||||
ASSERT_FALSE(other);
|
||||
ASSERT_FALSE(other.owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(other.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<void>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&other), nullptr);
|
||||
}
|
||||
|
||||
TEST(AnyDeathTest, VoidSelfMoveAssignment) {
|
||||
TEST(Any, VoidSelfMoveAssignment) {
|
||||
entt::any any{std::in_place_type<void>};
|
||||
|
||||
// avoid warnings due to self-assignment
|
||||
@@ -831,7 +849,7 @@ TEST(AnyDeathTest, VoidSelfMoveAssignment) {
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(any.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<void>());
|
||||
ASSERT_EQ(any.data(), nullptr);
|
||||
}
|
||||
|
||||
@@ -919,7 +937,7 @@ TEST(Any, Emplace) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::embedded);
|
||||
ASSERT_EQ(any.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(any), 2);
|
||||
}
|
||||
@@ -931,7 +949,7 @@ TEST(Any, EmplaceVoid) {
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(any.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<void>());
|
||||
}
|
||||
|
||||
TEST(Any, Reset) {
|
||||
@@ -940,14 +958,14 @@ TEST(Any, Reset) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::embedded);
|
||||
ASSERT_EQ(any.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<int>());
|
||||
|
||||
any.reset();
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(any.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<void>());
|
||||
|
||||
int value = 2;
|
||||
any.emplace<int &>(value);
|
||||
@@ -955,14 +973,14 @@ TEST(Any, Reset) {
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::ref);
|
||||
ASSERT_EQ(any.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<int>());
|
||||
|
||||
any.reset();
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(any.owner());
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(any.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<void>());
|
||||
}
|
||||
|
||||
TEST(Any, SBOSwap) {
|
||||
@@ -977,8 +995,8 @@ TEST(Any, SBOSwap) {
|
||||
ASSERT_EQ(lhs.policy(), entt::any_policy::embedded);
|
||||
ASSERT_EQ(rhs.policy(), entt::any_policy::embedded);
|
||||
|
||||
ASSERT_EQ(lhs.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(rhs.type(), entt::type_id<char>());
|
||||
ASSERT_EQ(lhs.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(rhs.info(), entt::type_id<char>());
|
||||
ASSERT_EQ(entt::any_cast<char>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int>(lhs), 2);
|
||||
@@ -1029,8 +1047,8 @@ TEST(Any, SBOWithNoSBOSwap) {
|
||||
ASSERT_EQ(lhs.policy(), entt::any_policy::embedded);
|
||||
ASSERT_EQ(rhs.policy(), entt::any_policy::dynamic);
|
||||
|
||||
ASSERT_EQ(lhs.type(), entt::type_id<char>());
|
||||
ASSERT_EQ(rhs.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(lhs.info(), entt::type_id<char>());
|
||||
ASSERT_EQ(rhs.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<fat>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(lhs), 'c');
|
||||
@@ -1050,8 +1068,8 @@ TEST(Any, SBOWithRefSwap) {
|
||||
ASSERT_EQ(lhs.policy(), entt::any_policy::embedded);
|
||||
ASSERT_EQ(rhs.policy(), entt::any_policy::ref);
|
||||
|
||||
ASSERT_EQ(lhs.type(), entt::type_id<char>());
|
||||
ASSERT_EQ(rhs.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(lhs.info(), entt::type_id<char>());
|
||||
ASSERT_EQ(rhs.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<int>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(lhs), 'c');
|
||||
@@ -1072,8 +1090,8 @@ TEST(Any, SBOWithConstRefSwap) {
|
||||
ASSERT_EQ(lhs.policy(), entt::any_policy::embedded);
|
||||
ASSERT_EQ(rhs.policy(), entt::any_policy::cref);
|
||||
|
||||
ASSERT_EQ(lhs.type(), entt::type_id<char>());
|
||||
ASSERT_EQ(rhs.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(lhs.info(), entt::type_id<char>());
|
||||
ASSERT_EQ(rhs.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<int>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(lhs), 'c');
|
||||
@@ -1091,7 +1109,7 @@ TEST(Any, SBOWithEmptySwap) {
|
||||
ASSERT_FALSE(lhs);
|
||||
ASSERT_FALSE(lhs.owner());
|
||||
ASSERT_EQ(lhs.policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(rhs.type(), entt::type_id<char>());
|
||||
ASSERT_EQ(rhs.info(), entt::type_id<char>());
|
||||
ASSERT_EQ(entt::any_cast<char>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<double>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(rhs), 'c');
|
||||
@@ -1101,7 +1119,7 @@ TEST(Any, SBOWithEmptySwap) {
|
||||
ASSERT_FALSE(rhs);
|
||||
ASSERT_TRUE(lhs.owner());
|
||||
ASSERT_EQ(lhs.policy(), entt::any_policy::embedded);
|
||||
ASSERT_EQ(lhs.type(), entt::type_id<char>());
|
||||
ASSERT_EQ(lhs.info(), entt::type_id<char>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(lhs), 'c');
|
||||
@@ -1116,7 +1134,7 @@ TEST(Any, SBOWithVoidSwap) {
|
||||
ASSERT_FALSE(lhs);
|
||||
ASSERT_FALSE(lhs.owner());
|
||||
ASSERT_EQ(lhs.policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(rhs.type(), entt::type_id<char>());
|
||||
ASSERT_EQ(rhs.info(), entt::type_id<char>());
|
||||
ASSERT_EQ(entt::any_cast<char>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<double>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(rhs), 'c');
|
||||
@@ -1126,7 +1144,7 @@ TEST(Any, SBOWithVoidSwap) {
|
||||
ASSERT_FALSE(rhs);
|
||||
ASSERT_TRUE(lhs.owner());
|
||||
ASSERT_EQ(lhs.policy(), entt::any_policy::embedded);
|
||||
ASSERT_EQ(lhs.type(), entt::type_id<char>());
|
||||
ASSERT_EQ(lhs.info(), entt::type_id<char>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<char>(lhs), 'c');
|
||||
@@ -1145,8 +1163,8 @@ TEST(Any, NoSBOWithRefSwap) {
|
||||
ASSERT_EQ(lhs.policy(), entt::any_policy::dynamic);
|
||||
ASSERT_EQ(rhs.policy(), entt::any_policy::ref);
|
||||
|
||||
ASSERT_EQ(lhs.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(rhs.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(lhs.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(rhs.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<int>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(lhs), (fat{.1, .2, .3, .4}));
|
||||
@@ -1167,8 +1185,8 @@ TEST(Any, NoSBOWithConstRefSwap) {
|
||||
ASSERT_EQ(lhs.policy(), entt::any_policy::dynamic);
|
||||
ASSERT_EQ(rhs.policy(), entt::any_policy::cref);
|
||||
|
||||
ASSERT_EQ(lhs.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(rhs.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(lhs.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(rhs.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(entt::any_cast<int>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(lhs), (fat{.1, .2, .3, .4}));
|
||||
@@ -1186,7 +1204,7 @@ TEST(Any, NoSBOWithEmptySwap) {
|
||||
ASSERT_FALSE(lhs);
|
||||
ASSERT_FALSE(lhs.owner());
|
||||
ASSERT_EQ(lhs.policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(rhs.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(rhs.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<fat>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<double>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(rhs), (fat{.1, .2, .3, .4}));
|
||||
@@ -1196,7 +1214,7 @@ TEST(Any, NoSBOWithEmptySwap) {
|
||||
ASSERT_FALSE(rhs);
|
||||
ASSERT_TRUE(lhs.owner());
|
||||
ASSERT_EQ(lhs.policy(), entt::any_policy::dynamic);
|
||||
ASSERT_EQ(lhs.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(lhs.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(lhs), (fat{.1, .2, .3, .4}));
|
||||
@@ -1211,7 +1229,7 @@ TEST(Any, NoSBOWithVoidSwap) {
|
||||
ASSERT_FALSE(lhs);
|
||||
ASSERT_FALSE(lhs.owner());
|
||||
ASSERT_EQ(lhs.policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(rhs.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(rhs.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<fat>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<double>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(rhs), (fat{.1, .2, .3, .4}));
|
||||
@@ -1221,7 +1239,7 @@ TEST(Any, NoSBOWithVoidSwap) {
|
||||
ASSERT_FALSE(rhs);
|
||||
ASSERT_TRUE(lhs.owner());
|
||||
ASSERT_EQ(lhs.policy(), entt::any_policy::dynamic);
|
||||
ASSERT_EQ(lhs.type(), entt::type_id<fat>());
|
||||
ASSERT_EQ(lhs.info(), entt::type_id<fat>());
|
||||
ASSERT_EQ(entt::any_cast<double>(&lhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(&rhs), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<fat>(lhs), (fat{.1, .2, .3, .4}));
|
||||
@@ -1443,6 +1461,53 @@ TEST(Any, CompareVoid) {
|
||||
ASSERT_FALSE(entt::any{} != any);
|
||||
}
|
||||
|
||||
TEST(Any, Data) {
|
||||
entt::any any{2};
|
||||
const auto &cany = any;
|
||||
entt::any empty{};
|
||||
|
||||
ASSERT_EQ(empty.data(), nullptr);
|
||||
ASSERT_NE(any.data(), nullptr);
|
||||
ASSERT_NE(cany.data(), nullptr);
|
||||
ASSERT_NE(any.as_ref().data(), nullptr);
|
||||
ASSERT_EQ(cany.as_ref().data(), nullptr);
|
||||
|
||||
ASSERT_EQ(empty.data<char>(), nullptr);
|
||||
ASSERT_EQ(any.data<char>(), nullptr);
|
||||
ASSERT_EQ(cany.data<char>(), nullptr);
|
||||
ASSERT_EQ(any.data<const char>(), nullptr);
|
||||
ASSERT_EQ(cany.data<const char>(), nullptr);
|
||||
ASSERT_EQ(any.as_ref().data<char>(), nullptr);
|
||||
ASSERT_EQ(cany.as_ref().data<char>(), nullptr);
|
||||
ASSERT_EQ(any.as_ref().data<const char>(), nullptr);
|
||||
ASSERT_EQ(cany.as_ref().data<const char>(), nullptr);
|
||||
|
||||
ASSERT_EQ(empty.data<int>(), nullptr);
|
||||
ASSERT_NE(any.data<int>(), nullptr);
|
||||
ASSERT_NE(cany.data<int>(), nullptr);
|
||||
ASSERT_NE(any.data<const int>(), nullptr);
|
||||
ASSERT_NE(cany.data<const int>(), nullptr);
|
||||
ASSERT_NE(any.as_ref().data<int>(), nullptr);
|
||||
ASSERT_EQ(cany.as_ref().data<int>(), nullptr);
|
||||
ASSERT_NE(any.as_ref().data<const int>(), nullptr);
|
||||
ASSERT_NE(cany.as_ref().data<const int>(), nullptr);
|
||||
|
||||
const auto &char_info = entt::type_id<char>();
|
||||
const auto &int_info = entt::type_id<int>();
|
||||
|
||||
ASSERT_EQ(empty.data(char_info), nullptr);
|
||||
ASSERT_EQ(any.data(char_info), nullptr);
|
||||
ASSERT_EQ(cany.data(char_info), nullptr);
|
||||
ASSERT_EQ(any.as_ref().data(char_info), nullptr);
|
||||
ASSERT_EQ(cany.as_ref().data(char_info), nullptr);
|
||||
|
||||
ASSERT_EQ(empty.data(int_info), nullptr);
|
||||
ASSERT_NE(any.data(int_info), nullptr);
|
||||
ASSERT_NE(cany.data(int_info), nullptr);
|
||||
ASSERT_NE(any.as_ref().data(int_info), nullptr);
|
||||
ASSERT_EQ(cany.as_ref().data(int_info), nullptr);
|
||||
}
|
||||
|
||||
TEST(Any, AnyCast) {
|
||||
entt::any any{2};
|
||||
const auto &cany = any;
|
||||
@@ -1552,7 +1617,7 @@ TEST(Any, NonCopyableType) {
|
||||
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::embedded);
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::cref);
|
||||
ASSERT_EQ(any.type(), other.type());
|
||||
ASSERT_EQ(any.info(), other.info());
|
||||
|
||||
ASSERT_FALSE(any.assign(other));
|
||||
ASSERT_FALSE(any.assign(std::move(other)));
|
||||
@@ -1609,7 +1674,7 @@ TEST(Any, NonMovableType) {
|
||||
|
||||
ASSERT_EQ(any.policy(), entt::any_policy::dynamic);
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::dynamic);
|
||||
ASSERT_EQ(any.type(), other.type());
|
||||
ASSERT_EQ(any.info(), other.info());
|
||||
|
||||
ASSERT_TRUE(any.assign(other));
|
||||
ASSERT_TRUE(any.assign(std::move(other)));
|
||||
@@ -1646,17 +1711,17 @@ TEST(Any, Array) {
|
||||
ASSERT_FALSE(copy);
|
||||
|
||||
// NOLINTBEGIN(*-avoid-c-arrays)
|
||||
ASSERT_EQ(any.type(), entt::type_id<int[1]>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<int[1]>());
|
||||
ASSERT_NE(entt::any_cast<int[1]>(&any), nullptr);
|
||||
ASSERT_EQ(entt::any_cast<int[2]>(&any), nullptr);
|
||||
// NOLINTEND(*-avoid-c-arrays)
|
||||
ASSERT_EQ(entt::any_cast<int *>(&any), nullptr);
|
||||
|
||||
// NOLINTNEXTLINE(*-avoid-c-arrays)
|
||||
entt::any_cast<int(&)[1]>(any)[0] = 2;
|
||||
entt::any_cast<int (&)[1]>(any)[0] = 2;
|
||||
|
||||
// NOLINTNEXTLINE(*-avoid-c-arrays)
|
||||
ASSERT_EQ(entt::any_cast<const int(&)[1]>(std::as_const(any))[0], 2);
|
||||
ASSERT_EQ(entt::any_cast<const int (&)[1]>(std::as_const(any))[0], 2);
|
||||
}
|
||||
|
||||
TEST(Any, CopyMoveReference) {
|
||||
@@ -1677,8 +1742,8 @@ TEST(Any, CopyMoveReference) {
|
||||
ASSERT_EQ(move.policy(), entt::any_policy::ref);
|
||||
ASSERT_EQ(copy.policy(), entt::any_policy::embedded);
|
||||
|
||||
ASSERT_EQ(move.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(copy.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(move.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(copy.info(), entt::type_id<int>());
|
||||
|
||||
ASSERT_EQ(std::as_const(move).data(), &value);
|
||||
ASSERT_NE(std::as_const(copy).data(), &value);
|
||||
@@ -1710,8 +1775,8 @@ TEST(Any, CopyMoveConstReference) {
|
||||
ASSERT_EQ(move.policy(), entt::any_policy::cref);
|
||||
ASSERT_EQ(copy.policy(), entt::any_policy::embedded);
|
||||
|
||||
ASSERT_EQ(move.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(copy.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(move.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(copy.info(), entt::type_id<int>());
|
||||
|
||||
ASSERT_EQ(std::as_const(move).data(), &value);
|
||||
ASSERT_NE(std::as_const(copy).data(), &value);
|
||||
@@ -1746,17 +1811,13 @@ TEST(Any, SboAlignment) {
|
||||
std::array<any_type, 2u> sbo = {over_aligned{}, over_aligned{}};
|
||||
const auto *data = sbo[0].data();
|
||||
|
||||
// NOLINTBEGIN(*-reinterpret-cast)
|
||||
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[0u].data()) % alignment) == 0u);
|
||||
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[1u].data()) % alignment) == 0u);
|
||||
// NOLINTEND(*-reinterpret-cast)
|
||||
|
||||
std::swap(sbo[0], sbo[1]);
|
||||
|
||||
// NOLINTBEGIN(*-reinterpret-cast)
|
||||
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[0u].data()) % alignment) == 0u);
|
||||
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[1u].data()) % alignment) == 0u);
|
||||
// NOLINTEND(*-reinterpret-cast)
|
||||
|
||||
ASSERT_NE(data, sbo[1].data());
|
||||
}
|
||||
@@ -1768,17 +1829,13 @@ TEST(Any, NoSboAlignment) {
|
||||
std::array<any_type, 2u> nosbo = {over_aligned{}, over_aligned{}};
|
||||
const auto *data = nosbo[0].data();
|
||||
|
||||
// NOLINTBEGIN(*-reinterpret-cast)
|
||||
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[0u].data()) % alignment) == 0u);
|
||||
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[1u].data()) % alignment) == 0u);
|
||||
// NOLINTEND(*-reinterpret-cast)
|
||||
|
||||
std::swap(nosbo[0], nosbo[1]);
|
||||
|
||||
// NOLINTBEGIN(*-reinterpret-cast)
|
||||
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[0u].data()) % alignment) == 0u);
|
||||
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[1u].data()) % alignment) == 0u);
|
||||
// NOLINTEND(*-reinterpret-cast)
|
||||
|
||||
ASSERT_EQ(data, nosbo[1].data());
|
||||
}
|
||||
@@ -1792,13 +1849,13 @@ TEST(Any, DeducedArrayType) {
|
||||
entt::any any{"array of char"};
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_EQ(any.type(), entt::type_id<const char *>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<const char *>());
|
||||
ASSERT_EQ((std::strcmp("array of char", entt::any_cast<const char *>(any))), 0);
|
||||
|
||||
any = "another array of char";
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_EQ(any.type(), entt::type_id<const char *>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<const char *>());
|
||||
ASSERT_EQ((std::strcmp("another array of char", entt::any_cast<const char *>(any))), 0);
|
||||
}
|
||||
|
||||
@@ -1807,6 +1864,6 @@ TEST(Any, ClassLevelNewDelete) {
|
||||
entt::any any{std::in_place_type<test::new_delete>, *std::make_unique<test::new_delete>(test::new_delete{3})};
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_EQ(any.type(), entt::type_id<test::new_delete>());
|
||||
ASSERT_EQ(any.info(), entt::type_id<test::new_delete>());
|
||||
ASSERT_EQ(entt::any_cast<const test::new_delete &>(any).value, 3);
|
||||
}
|
||||
|
||||
@@ -177,7 +177,6 @@ TEST(UninitializedConstructUsingAllocator, NoUsesAllocatorConstruction) {
|
||||
alignas(int) std::array<std::byte, sizeof(int)> storage{};
|
||||
const std::allocator<int> allocator{};
|
||||
|
||||
// NOLINTNEXTLINE(*-reinterpret-cast)
|
||||
int *value = entt::uninitialized_construct_using_allocator(reinterpret_cast<int *>(storage.data()), allocator, 1);
|
||||
|
||||
ASSERT_EQ(*value, 1);
|
||||
@@ -193,7 +192,6 @@ TEST(UninitializedConstructUsingAllocator, UsesAllocatorConstruction) {
|
||||
const std::pmr::polymorphic_allocator<string_type> allocator{&memory_resource};
|
||||
alignas(string_type) std::array<std::byte, sizeof(string_type)> storage{};
|
||||
|
||||
// NOLINTNEXTLINE(*-reinterpret-cast)
|
||||
string_type *value = entt::uninitialized_construct_using_allocator(reinterpret_cast<string_type *>(storage.data()), allocator, test::tracked_memory_resource::default_value);
|
||||
|
||||
ASSERT_GT(memory_resource.do_allocate_counter(), 0u);
|
||||
|
||||
@@ -51,53 +51,47 @@ using EntityTypes = ::testing::Types<entt::entity, test::entity, test::other_ent
|
||||
TYPED_TEST_SUITE(Component, EntityTypes, );
|
||||
|
||||
TYPED_TEST(Component, VoidType) {
|
||||
using entity_type = typename TestFixture::entity_type;
|
||||
using traits_type = entt::component_traits<void, entity_type>;
|
||||
using traits_type = entt::component_traits<void, typename TestFixture::entity_type>;
|
||||
|
||||
ASSERT_FALSE(traits_type::in_place_delete);
|
||||
ASSERT_EQ(traits_type::page_size, 0u);
|
||||
}
|
||||
|
||||
TYPED_TEST(Component, Empty) {
|
||||
using entity_type = typename TestFixture::entity_type;
|
||||
using traits_type = entt::component_traits<test::empty, entity_type>;
|
||||
using traits_type = entt::component_traits<test::empty, typename TestFixture::entity_type>;
|
||||
|
||||
ASSERT_FALSE(traits_type::in_place_delete);
|
||||
ASSERT_EQ(traits_type::page_size, 0u);
|
||||
}
|
||||
|
||||
TYPED_TEST(Component, NonEmpty) {
|
||||
using entity_type = typename TestFixture::entity_type;
|
||||
using traits_type = entt::component_traits<test::boxed_int, entity_type>;
|
||||
using traits_type = entt::component_traits<test::boxed_int, typename TestFixture::entity_type>;
|
||||
|
||||
ASSERT_FALSE(traits_type::in_place_delete);
|
||||
ASSERT_EQ(traits_type::page_size, ENTT_PACKED_PAGE);
|
||||
}
|
||||
|
||||
TYPED_TEST(Component, NonMovable) {
|
||||
using entity_type = typename TestFixture::entity_type;
|
||||
using traits_type = entt::component_traits<test::non_movable, entity_type>;
|
||||
using traits_type = entt::component_traits<test::non_movable, typename TestFixture::entity_type>;
|
||||
|
||||
ASSERT_TRUE(traits_type::in_place_delete);
|
||||
ASSERT_EQ(traits_type::page_size, ENTT_PACKED_PAGE);
|
||||
}
|
||||
|
||||
TYPED_TEST(Component, SelfContained) {
|
||||
using entity_type = typename TestFixture::entity_type;
|
||||
using traits_type = entt::component_traits<self_contained, entity_type>;
|
||||
using traits_type = entt::component_traits<self_contained, typename TestFixture::entity_type>;
|
||||
|
||||
ASSERT_TRUE(traits_type::in_place_delete);
|
||||
ASSERT_EQ(traits_type::page_size, 4u);
|
||||
}
|
||||
|
||||
TYPED_TEST(Component, TraitsBased) {
|
||||
using entity_type = typename TestFixture::entity_type;
|
||||
using traits_type = entt::component_traits<traits_based, entity_type>;
|
||||
using traits_type = entt::component_traits<traits_based, typename TestFixture::entity_type>;
|
||||
|
||||
if constexpr(std::is_same_v<entity_type, entt::entity>) {
|
||||
if constexpr(std::is_same_v<typename traits_type::entity_type, entt::entity>) {
|
||||
ASSERT_TRUE(traits_type::in_place_delete);
|
||||
ASSERT_EQ(traits_type::page_size, 8u);
|
||||
} else if constexpr(std::is_same_v<entity_type, test::entity>) {
|
||||
} else if constexpr(std::is_same_v<typename traits_type::entity_type, test::entity>) {
|
||||
ASSERT_FALSE(traits_type::in_place_delete);
|
||||
ASSERT_EQ(traits_type::page_size, 16u);
|
||||
} else {
|
||||
|
||||
@@ -93,7 +93,7 @@ TYPED_TEST(BasicHandle, Storage) {
|
||||
|
||||
ASSERT_NE(handle.storage().begin(), handle.storage().end());
|
||||
ASSERT_EQ(++handle.storage().begin(), handle.storage().end());
|
||||
ASSERT_EQ(handle.storage().begin()->second.type(), entt::type_id<int>());
|
||||
ASSERT_EQ(handle.storage().begin()->second.info(), entt::type_id<int>());
|
||||
}
|
||||
|
||||
ENTT_DEBUG_TYPED_TEST(BasicHandleDeathTest, Storage) {
|
||||
@@ -187,8 +187,8 @@ TEST(BasicHandle, Destruction) {
|
||||
ENTT_DEBUG_TEST(BasicHandleDeathTest, Destruction) {
|
||||
entt::handle handle{};
|
||||
|
||||
ASSERT_DEATH(handle.destroy(0u);, "");
|
||||
ASSERT_DEATH(handle.destroy();, "");
|
||||
ASSERT_DEATH(handle.destroy(0u), "");
|
||||
ASSERT_DEATH(handle.destroy(), "");
|
||||
}
|
||||
|
||||
TEST(BasicHandle, Emplace) {
|
||||
@@ -207,7 +207,7 @@ TEST(BasicHandle, Emplace) {
|
||||
ENTT_DEBUG_TEST(BasicHandleDeathTest, Emplace) {
|
||||
const entt::handle handle{};
|
||||
|
||||
ASSERT_DEATH(handle.emplace<int>(3);, "");
|
||||
ASSERT_DEATH(handle.emplace<int>(3), "");
|
||||
}
|
||||
|
||||
TEST(BasicHandle, EmplaceOrReplace) {
|
||||
@@ -230,7 +230,7 @@ TEST(BasicHandle, EmplaceOrReplace) {
|
||||
ENTT_DEBUG_TEST(BasicHandleDeathTest, EmplaceOrReplace) {
|
||||
const entt::handle handle{};
|
||||
|
||||
ASSERT_DEATH(handle.emplace_or_replace<int>(3);, "");
|
||||
ASSERT_DEATH(handle.emplace_or_replace<int>(3), "");
|
||||
}
|
||||
|
||||
TEST(BasicHandle, Patch) {
|
||||
@@ -249,7 +249,7 @@ TEST(BasicHandle, Patch) {
|
||||
ENTT_DEBUG_TEST(BasicHandleDeathTest, Patch) {
|
||||
const entt::handle handle{};
|
||||
|
||||
ASSERT_DEATH(handle.patch<int>([](auto &comp) { comp = 1; });, "");
|
||||
ASSERT_DEATH(handle.patch<int>([](auto &comp) { comp = 1; }), "");
|
||||
}
|
||||
|
||||
TEST(BasicHandle, Replace) {
|
||||
@@ -268,7 +268,7 @@ TEST(BasicHandle, Replace) {
|
||||
ENTT_DEBUG_TEST(BasicHandleDeathTest, Replace) {
|
||||
const entt::handle handle{};
|
||||
|
||||
ASSERT_DEATH(handle.replace<int>(3);, "");
|
||||
ASSERT_DEATH(handle.replace<int>(3), "");
|
||||
}
|
||||
|
||||
TEST(BasicHandle, Remove) {
|
||||
@@ -291,7 +291,7 @@ TEST(BasicHandle, Remove) {
|
||||
ENTT_DEBUG_TEST(BasicHandleDeathTest, Remove) {
|
||||
const entt::handle handle{};
|
||||
|
||||
ASSERT_DEATH(handle.remove<int>();, "");
|
||||
ASSERT_DEATH(handle.remove<int>(), "");
|
||||
}
|
||||
|
||||
TEST(BasicHandle, Erase) {
|
||||
@@ -311,7 +311,7 @@ TEST(BasicHandle, Erase) {
|
||||
ENTT_DEBUG_TEST(BasicHandleDeathTest, Erase) {
|
||||
const entt::handle handle{};
|
||||
|
||||
ASSERT_DEATH(handle.erase<int>();, "");
|
||||
ASSERT_DEATH(handle.erase<int>(), "");
|
||||
}
|
||||
|
||||
TYPED_TEST(BasicHandle, AllAnyOf) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <entt/core/hashed_string.hpp>
|
||||
#include <entt/entity/component.hpp>
|
||||
#include <entt/entity/entity.hpp>
|
||||
#include <entt/entity/group.hpp>
|
||||
#include <entt/entity/helper.hpp>
|
||||
#include <entt/entity/mixin.hpp>
|
||||
#include <entt/entity/registry.hpp>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/core/type_info.hpp>
|
||||
#include <entt/entity/group.hpp>
|
||||
@@ -24,6 +25,9 @@ struct clazz {
|
||||
static void ro_int_with_payload(const clazz &, entt::view<entt::get_t<const int>>) {}
|
||||
static void ro_char_with_payload(const clazz &, entt::group<entt::owned_t<const char>>) {}
|
||||
static void ro_int_char_with_payload(clazz &, entt::view<entt::get_t<const int, const char>>) {}
|
||||
|
||||
static void const_registry_first(const entt::registry &, entt::view<entt::get_t<int>>) {}
|
||||
static void const_registry_second(const entt::registry &, entt::view<entt::get_t<double>>) {}
|
||||
};
|
||||
|
||||
void to_args_integrity(entt::view<entt::get_t<int>> view, std::size_t &value, entt::registry &) {
|
||||
@@ -381,6 +385,35 @@ TEST(Organizer, SyncPoint) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Organizer, ConstRegistry) {
|
||||
entt::organizer organizer;
|
||||
entt::registry registry;
|
||||
|
||||
organizer.emplace<&clazz::const_registry_first>("first");
|
||||
organizer.emplace<&clazz::const_registry_second>("second");
|
||||
|
||||
const auto graph = organizer.graph();
|
||||
|
||||
ASSERT_EQ(graph.size(), 2u);
|
||||
|
||||
ASSERT_STREQ(graph[0u].name(), "first");
|
||||
ASSERT_STREQ(graph[1u].name(), "second");
|
||||
|
||||
ASSERT_TRUE(graph[0u].top_level());
|
||||
ASSERT_TRUE(graph[1u].top_level());
|
||||
|
||||
ASSERT_EQ(graph[0u].in_edges().size(), 0u);
|
||||
ASSERT_EQ(graph[1u].in_edges().size(), 0u);
|
||||
|
||||
ASSERT_EQ(graph[0u].out_edges().size(), 0u);
|
||||
ASSERT_EQ(graph[1u].out_edges().size(), 0u);
|
||||
|
||||
for(auto &&vertex: graph) {
|
||||
typename entt::organizer::function_type *cb = vertex.callback();
|
||||
ASSERT_NO_THROW(cb(vertex.data(), registry));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Organizer, Override) {
|
||||
entt::organizer organizer;
|
||||
|
||||
|
||||
@@ -56,13 +56,13 @@ TYPED_TEST(ReactiveMixin, Constructors) {
|
||||
|
||||
ASSERT_EQ(pool.policy(), entt::deletion_policy{traits_type::in_place_delete});
|
||||
ASSERT_NO_THROW([[maybe_unused]] auto alloc = pool.get_allocator());
|
||||
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<value_type>());
|
||||
|
||||
pool = entt::reactive_mixin<entt::storage<value_type>>{std::allocator<value_type>{}};
|
||||
|
||||
ASSERT_EQ(pool.policy(), entt::deletion_policy{traits_type::in_place_delete});
|
||||
ASSERT_NO_THROW([[maybe_unused]] auto alloc = pool.get_allocator());
|
||||
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<value_type>());
|
||||
}
|
||||
|
||||
TYPED_TEST(ReactiveMixin, Move) {
|
||||
@@ -80,7 +80,7 @@ TYPED_TEST(ReactiveMixin, Move) {
|
||||
static_assert(std::is_move_assignable_v<decltype(pool)>, "Move assignable type required");
|
||||
|
||||
ASSERT_TRUE(pool.contains(entity[0u]));
|
||||
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<value_type>());
|
||||
|
||||
entt::reactive_mixin<entt::storage<value_type>> other{std::move(pool)};
|
||||
|
||||
@@ -89,7 +89,7 @@ TYPED_TEST(ReactiveMixin, Move) {
|
||||
ASSERT_TRUE(pool.empty());
|
||||
ASSERT_FALSE(other.empty());
|
||||
|
||||
ASSERT_EQ(other.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<value_type>());
|
||||
|
||||
ASSERT_EQ(other.index(entity[0u]), 0u);
|
||||
ASSERT_EQ(&other.registry(), ®istry);
|
||||
@@ -149,8 +149,8 @@ TYPED_TEST(ReactiveMixin, Swap) {
|
||||
|
||||
pool.swap(other);
|
||||
|
||||
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<value_type>());
|
||||
|
||||
ASSERT_EQ(pool.size(), 1u);
|
||||
ASSERT_EQ(other.size(), 1u);
|
||||
|
||||
@@ -309,13 +309,15 @@ TEST(Registry, Swap) {
|
||||
other.erase<int>(entity);
|
||||
|
||||
registry = {};
|
||||
registry.emplace<int>(registry.create(entity));
|
||||
const auto reboot = registry.create();
|
||||
registry.emplace<int>(reboot);
|
||||
|
||||
ASSERT_EQ(entity, reboot);
|
||||
ASSERT_EQ(test.parent, &other);
|
||||
|
||||
registry.swap(other);
|
||||
registry.emplace<int>(entity);
|
||||
registry.emplace<int>(registry.create(entity));
|
||||
registry.emplace<int>(reboot);
|
||||
registry.emplace<int>(registry.create(reboot));
|
||||
|
||||
ASSERT_EQ(test.parent, ®istry);
|
||||
}
|
||||
@@ -466,9 +468,9 @@ TEST(Registry, RegistryStorageIteratorConversion) {
|
||||
testing::StaticAssertTypeEq<decltype(*cit), std::pair<entt::id_type, const entt::sparse_set &>>();
|
||||
|
||||
ASSERT_EQ(it->first, entt::type_id<int>().hash());
|
||||
ASSERT_EQ((*it).second.type(), entt::type_id<int>());
|
||||
ASSERT_EQ((*it).second.info(), entt::type_id<int>());
|
||||
ASSERT_EQ(it->first, cit->first);
|
||||
ASSERT_EQ((*it).second.type(), (*cit).second.type());
|
||||
ASSERT_EQ((*it).second.info(), (*cit).second.info());
|
||||
|
||||
ASSERT_EQ(it - cit, 0);
|
||||
ASSERT_EQ(cit - it, 0);
|
||||
@@ -973,7 +975,7 @@ TEST(Registry, EmplaceEmpty) {
|
||||
|
||||
ASSERT_FALSE(registry.all_of<test::empty>(entity));
|
||||
|
||||
registry.emplace<test::empty>(entity, 4);
|
||||
registry.emplace<test::empty>(entity);
|
||||
|
||||
ASSERT_TRUE(registry.all_of<test::empty>(entity));
|
||||
}
|
||||
@@ -1442,7 +1444,7 @@ TEST(Registry, EraseIf) {
|
||||
ASSERT_FALSE(registry.storage<int>("other"_hs).contains(entity));
|
||||
ASSERT_TRUE(registry.storage<char>().contains(entity));
|
||||
|
||||
registry.erase_if(entity, [](auto, const auto &storage) { return storage.type() == entt::type_id<char>(); });
|
||||
registry.erase_if(entity, [](auto, const auto &storage) { return storage.info() == entt::type_id<char>(); });
|
||||
|
||||
ASSERT_TRUE(registry.storage<int>().contains(entity));
|
||||
ASSERT_FALSE(registry.storage<int>("other"_hs).contains(entity));
|
||||
@@ -2353,6 +2355,14 @@ TEST(Registry, Context) {
|
||||
ASSERT_EQ(ctx.find<const int>(), cctx.find<int>());
|
||||
ASSERT_EQ(ctx.get<int>(), cctx.get<const int>());
|
||||
ASSERT_EQ(ctx.get<int>(), 0);
|
||||
|
||||
ASSERT_TRUE(ctx.contains<char>());
|
||||
ASSERT_TRUE(ctx.contains<int>());
|
||||
|
||||
ctx.clear();
|
||||
|
||||
ASSERT_FALSE(ctx.contains<char>());
|
||||
ASSERT_FALSE(ctx.contains<int>());
|
||||
}
|
||||
|
||||
TEST(Registry, ContextHint) {
|
||||
@@ -2546,8 +2556,8 @@ TEST(Registry, VoidType) {
|
||||
ASSERT_FALSE(registry.storage<void>("custom"_hs).empty());
|
||||
ASSERT_TRUE(registry.storage<void>("custom"_hs).contains(entity));
|
||||
|
||||
ASSERT_EQ(registry.storage<void>().type(), entt::type_id<void>());
|
||||
ASSERT_EQ(registry.storage<void>("custom"_hs).type(), entt::type_id<void>());
|
||||
ASSERT_EQ(registry.storage<void>().info(), entt::type_id<void>());
|
||||
ASSERT_EQ(registry.storage<void>("custom"_hs).info(), entt::type_id<void>());
|
||||
}
|
||||
|
||||
TEST(Registry, NoEtoType) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
@@ -73,6 +74,29 @@ TYPED_TEST(RuntimeView, Functionalities) {
|
||||
ASSERT_EQ(view.begin(), view.end());
|
||||
}
|
||||
|
||||
TYPED_TEST(RuntimeView, InvalidView) {
|
||||
using runtime_view_type = typename TestFixture::type;
|
||||
|
||||
runtime_view_type view{};
|
||||
|
||||
ASSERT_FALSE(view);
|
||||
|
||||
ASSERT_EQ(view.size_hint(), 0u);
|
||||
ASSERT_FALSE(view.contains(entt::null));
|
||||
|
||||
ASSERT_EQ(view.begin(), typename decltype(view)::iterator{});
|
||||
ASSERT_EQ(view.begin(), view.end());
|
||||
|
||||
view.each([](const entt::entity) { FAIL(); });
|
||||
|
||||
entt::storage<int> storage;
|
||||
|
||||
storage.emplace(entt::entity{});
|
||||
view.iterate(storage);
|
||||
|
||||
ASSERT_TRUE(view);
|
||||
}
|
||||
|
||||
TYPED_TEST(RuntimeView, Constructors) {
|
||||
using runtime_view_type = typename TestFixture::type;
|
||||
|
||||
@@ -322,7 +346,7 @@ TYPED_TEST(RuntimeView, EachWithHoles) {
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST(RuntimeView, ExcludedComponents) {
|
||||
TYPED_TEST(RuntimeView, Exclude) {
|
||||
using runtime_view_type = typename TestFixture::type;
|
||||
|
||||
std::tuple<entt::storage<int>, entt::storage<char>> storage{};
|
||||
@@ -383,7 +407,7 @@ TYPED_TEST(RuntimeView, StableType) {
|
||||
ASSERT_EQ(view.size_hint(), 1u);
|
||||
}
|
||||
|
||||
TYPED_TEST(RuntimeView, StableTypeWithExcludedComponent) {
|
||||
TYPED_TEST(RuntimeView, StableTypeWithExclude) {
|
||||
using runtime_view_type = typename TestFixture::type;
|
||||
|
||||
constexpr entt::entity tombstone = entt::tombstone;
|
||||
@@ -418,3 +442,134 @@ TYPED_TEST(RuntimeView, StableTypeWithExcludedComponent) {
|
||||
ASSERT_EQ(entt, entity[1u]);
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST(RuntimeView, SameStorageTypes) {
|
||||
using runtime_view_type = typename TestFixture::type;
|
||||
|
||||
std::tuple<entt::storage<int>, entt::storage<int>> storage{};
|
||||
const std::array entity{entt::entity{1}, entt::entity{3}};
|
||||
runtime_view_type view{};
|
||||
|
||||
std::get<0>(storage).emplace(entity[0u], 2);
|
||||
|
||||
std::get<1>(storage).emplace(entity[0u], 3);
|
||||
std::get<1>(storage).emplace(entity[1u], 1);
|
||||
|
||||
view.iterate(std::get<0>(storage)).iterate(std::get<1>(storage));
|
||||
|
||||
ASSERT_TRUE(view.contains(entity[0u]));
|
||||
ASSERT_FALSE(view.contains(entity[1u]));
|
||||
|
||||
for(auto entt: view) {
|
||||
ASSERT_EQ(entt, entity[0u]);
|
||||
}
|
||||
|
||||
view.each([&](auto entt) {
|
||||
ASSERT_EQ(entt, entity[0u]);
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST(RuntimeView, StorageEntity) {
|
||||
using runtime_view_type = typename TestFixture::type;
|
||||
|
||||
std::tuple<entt::storage<entt::entity>, entt::storage<char>> storage{};
|
||||
const std::array entity{std::get<0>(storage).generate(), std::get<0>(storage).generate()};
|
||||
runtime_view_type view{};
|
||||
|
||||
std::get<1>(storage).emplace(entity[0u]);
|
||||
std::get<1>(storage).emplace(entity[1u]);
|
||||
|
||||
std::get<1>(storage).erase(entity[0u]);
|
||||
std::get<0>(storage).erase(entity[0u]);
|
||||
std::get<0>(storage).bump(entity[0u]);
|
||||
|
||||
view.iterate(std::get<0>(storage)).iterate(std::get<1>(storage));
|
||||
|
||||
ASSERT_FALSE(view.contains(entity[0u]));
|
||||
ASSERT_TRUE(view.contains(entity[1u]));
|
||||
|
||||
ASSERT_EQ(view.size_hint(), 1u);
|
||||
ASSERT_NE(view.begin(), view.end());
|
||||
|
||||
ASSERT_EQ(std::distance(view.begin(), view.end()), 1);
|
||||
ASSERT_EQ(*view.begin(), entity[1u]);
|
||||
|
||||
for(auto entt: view) {
|
||||
ASSERT_EQ(entt, entity[1u]);
|
||||
}
|
||||
|
||||
view.each([&entity](auto entt) {
|
||||
ASSERT_EQ(entt, entity[1u]);
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST(RuntimeView, StorageEntityWithExclude) {
|
||||
using runtime_view_type = typename TestFixture::type;
|
||||
|
||||
std::tuple<entt::storage<entt::entity>, entt::storage<int>, entt::storage<char>> storage{};
|
||||
const std::array entity{std::get<0>(storage).generate(), std::get<0>(storage).generate(), std::get<0>(storage).generate()};
|
||||
runtime_view_type view{};
|
||||
|
||||
std::get<1>(storage).emplace(entity[0u]);
|
||||
std::get<1>(storage).emplace(entity[1u]);
|
||||
std::get<1>(storage).emplace(entity[2u]);
|
||||
|
||||
std::get<2>(storage).emplace(entity[2u]);
|
||||
|
||||
std::get<1>(storage).erase(entity[0u]);
|
||||
std::get<0>(storage).erase(entity[0u]);
|
||||
std::get<0>(storage).bump(entity[0u]);
|
||||
|
||||
view.iterate(std::get<0>(storage)).iterate(std::get<1>(storage)).exclude(std::get<2>(storage));
|
||||
|
||||
ASSERT_FALSE(view.contains(entity[0u]));
|
||||
ASSERT_TRUE(view.contains(entity[1u]));
|
||||
ASSERT_FALSE(view.contains(entity[2u]));
|
||||
|
||||
ASSERT_EQ(view.size_hint(), 2u);
|
||||
ASSERT_NE(view.begin(), view.end());
|
||||
|
||||
ASSERT_EQ(std::distance(view.begin(), view.end()), 1);
|
||||
ASSERT_EQ(*view.begin(), entity[1u]);
|
||||
|
||||
for(auto entt: view) {
|
||||
ASSERT_EQ(entt, entity[1u]);
|
||||
}
|
||||
|
||||
view.each([&entity](auto entt, auto &&...) {
|
||||
ASSERT_EQ(entt, entity[1u]);
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST(RuntimeView, StorageEntityExcludeOnly) {
|
||||
using runtime_view_type = typename TestFixture::type;
|
||||
|
||||
std::tuple<entt::storage<entt::entity>, entt::storage<int>> storage{};
|
||||
const std::array entity{std::get<0>(storage).generate(), std::get<0>(storage).generate(), std::get<0>(storage).generate()};
|
||||
runtime_view_type view{};
|
||||
|
||||
std::get<1>(storage).emplace(entity[2u]);
|
||||
|
||||
std::get<0>(storage).erase(entity[0u]);
|
||||
std::get<0>(storage).bump(entity[0u]);
|
||||
|
||||
view.iterate(std::get<0>(storage)).exclude(std::get<1>(storage));
|
||||
|
||||
ASSERT_FALSE(view.contains(entity[0u]));
|
||||
ASSERT_TRUE(view.contains(entity[1u]));
|
||||
ASSERT_FALSE(view.contains(entity[2u]));
|
||||
|
||||
ASSERT_EQ(view.size_hint(), 2u);
|
||||
ASSERT_NE(view.begin(), view.end());
|
||||
|
||||
ASSERT_EQ(std::distance(view.begin(), view.end()), 1);
|
||||
ASSERT_EQ(*view.begin(), entity[1u]);
|
||||
|
||||
for(auto entt: view) {
|
||||
ASSERT_EQ(entt, entity[1u]);
|
||||
}
|
||||
|
||||
view.each([&entity](auto entt) {
|
||||
ASSERT_EQ(entt, entity[1u]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ TEST(SighMixin, VoidType) {
|
||||
|
||||
pool.emplace(entity);
|
||||
|
||||
ASSERT_EQ(pool.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<void>());
|
||||
ASSERT_TRUE(pool.contains(entity));
|
||||
|
||||
entt::sigh_mixin<entt::storage<void>> other{std::move(pool)};
|
||||
@@ -336,7 +336,7 @@ TYPED_TEST(SighMixin, Move) {
|
||||
static_assert(std::is_move_constructible_v<decltype(pool)>, "Move constructible type required");
|
||||
static_assert(std::is_move_assignable_v<decltype(pool)>, "Move assignable type required");
|
||||
|
||||
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<value_type>());
|
||||
|
||||
entt::sigh_mixin<entt::storage<value_type>> other{std::move(pool)};
|
||||
|
||||
@@ -345,7 +345,7 @@ TYPED_TEST(SighMixin, Move) {
|
||||
ASSERT_TRUE(pool.empty());
|
||||
ASSERT_FALSE(other.empty());
|
||||
|
||||
ASSERT_EQ(other.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<value_type>());
|
||||
|
||||
ASSERT_EQ(other.index(entt::entity{3}), 0u);
|
||||
ASSERT_EQ(other.get(entt::entity{3}), value_type{3});
|
||||
@@ -408,8 +408,8 @@ TYPED_TEST(SighMixin, Swap) {
|
||||
|
||||
pool.swap(other);
|
||||
|
||||
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<value_type>());
|
||||
|
||||
ASSERT_EQ(pool.size(), 1u + traits_type::in_place_delete);
|
||||
ASSERT_EQ(other.size(), 1u);
|
||||
|
||||
@@ -58,7 +58,7 @@ TYPED_TEST(SparseSet, Constructors) {
|
||||
|
||||
ASSERT_EQ(set.policy(), entt::deletion_policy::swap_and_pop);
|
||||
ASSERT_NO_THROW([[maybe_unused]] auto alloc = set.get_allocator());
|
||||
ASSERT_EQ(set.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(set.info(), entt::type_id<void>());
|
||||
|
||||
set = sparse_set_type{allocator_type{}};
|
||||
|
||||
@@ -70,7 +70,7 @@ TYPED_TEST(SparseSet, Constructors) {
|
||||
|
||||
ASSERT_EQ(set.policy(), policy);
|
||||
ASSERT_NO_THROW([[maybe_unused]] auto alloc = set.get_allocator());
|
||||
ASSERT_EQ(set.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(set.info(), entt::type_id<void>());
|
||||
|
||||
set = sparse_set_type{entt::type_id<int>(), policy, allocator_type{}};
|
||||
|
||||
|
||||
@@ -96,13 +96,13 @@ TYPED_TEST(Storage, Constructors) {
|
||||
|
||||
ASSERT_EQ(pool.policy(), entt::deletion_policy{traits_type::in_place_delete});
|
||||
ASSERT_NO_THROW([[maybe_unused]] auto alloc = pool.get_allocator());
|
||||
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<value_type>());
|
||||
|
||||
pool = entt::storage<value_type>{std::allocator<value_type>{}};
|
||||
|
||||
ASSERT_EQ(pool.policy(), entt::deletion_policy{traits_type::in_place_delete});
|
||||
ASSERT_NO_THROW([[maybe_unused]] auto alloc = pool.get_allocator());
|
||||
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<value_type>());
|
||||
}
|
||||
|
||||
TYPED_TEST(Storage, Move) {
|
||||
@@ -123,7 +123,7 @@ TYPED_TEST(Storage, Move) {
|
||||
ASSERT_TRUE(pool.empty());
|
||||
ASSERT_FALSE(other.empty());
|
||||
|
||||
ASSERT_EQ(other.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.index(entity[0u]), 0u);
|
||||
ASSERT_EQ(other.get(entity[0u]), value_type{3});
|
||||
|
||||
@@ -134,7 +134,7 @@ TYPED_TEST(Storage, Move) {
|
||||
ASSERT_TRUE(other.empty());
|
||||
ASSERT_FALSE(extended.empty());
|
||||
|
||||
ASSERT_EQ(extended.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(extended.info(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(extended.index(entity[0u]), 0u);
|
||||
ASSERT_EQ(extended.get(entity[0u]), value_type{3});
|
||||
|
||||
@@ -145,7 +145,7 @@ TYPED_TEST(Storage, Move) {
|
||||
ASSERT_TRUE(other.empty());
|
||||
ASSERT_TRUE(extended.empty());
|
||||
|
||||
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.index(entity[0u]), 0u);
|
||||
ASSERT_EQ(pool.get(entity[0u]), value_type{3});
|
||||
|
||||
@@ -157,7 +157,7 @@ TYPED_TEST(Storage, Move) {
|
||||
ASSERT_FALSE(pool.empty());
|
||||
ASSERT_FALSE(other.empty());
|
||||
|
||||
ASSERT_EQ(other.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.index(entity[0u]), 0u);
|
||||
ASSERT_EQ(other.get(entity[0u]), value_type{3});
|
||||
}
|
||||
@@ -169,8 +169,8 @@ TYPED_TEST(Storage, Swap) {
|
||||
entt::storage<value_type> pool;
|
||||
entt::storage<value_type> other;
|
||||
|
||||
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<value_type>());
|
||||
|
||||
pool.emplace(entt::entity{4}, 1);
|
||||
|
||||
@@ -183,8 +183,8 @@ TYPED_TEST(Storage, Swap) {
|
||||
|
||||
pool.swap(other);
|
||||
|
||||
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<value_type>());
|
||||
|
||||
ASSERT_EQ(pool.size(), 1u + traits_type::in_place_delete);
|
||||
ASSERT_EQ(other.size(), 1u);
|
||||
@@ -721,8 +721,8 @@ TEST(Storage, TryEmplaceNonDefaultConstructible) {
|
||||
entt::sparse_set &base = pool;
|
||||
const std::array entity{entt::entity{1}, entt::entity{3}};
|
||||
|
||||
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.type(), base.type());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.info(), base.info());
|
||||
|
||||
ASSERT_FALSE(pool.contains(entity[0u]));
|
||||
ASSERT_FALSE(pool.contains(entity[1u]));
|
||||
@@ -764,8 +764,8 @@ TEST(Storage, TryEmplaceNonCopyConstructible) {
|
||||
entt::sparse_set &base = pool;
|
||||
const std::array entity{entt::entity{1}, entt::entity{3}};
|
||||
|
||||
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.type(), base.type());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.info(), base.info());
|
||||
|
||||
ASSERT_FALSE(pool.contains(entity[0u]));
|
||||
ASSERT_FALSE(pool.contains(entity[1u]));
|
||||
|
||||
@@ -19,13 +19,13 @@ TEST(StorageEntity, Constructors) {
|
||||
|
||||
ASSERT_EQ(pool.policy(), entt::deletion_policy::swap_only);
|
||||
ASSERT_NO_THROW([[maybe_unused]] auto alloc = pool.get_allocator());
|
||||
ASSERT_EQ(pool.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<void>());
|
||||
|
||||
pool = entt::storage<entt::entity>{std::allocator<entt::entity>{}};
|
||||
|
||||
ASSERT_EQ(pool.policy(), entt::deletion_policy::swap_only);
|
||||
ASSERT_NO_THROW([[maybe_unused]] auto alloc = pool.get_allocator());
|
||||
ASSERT_EQ(pool.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<void>());
|
||||
}
|
||||
|
||||
TEST(StorageEntity, Move) {
|
||||
@@ -44,7 +44,7 @@ TEST(StorageEntity, Move) {
|
||||
ASSERT_TRUE(pool.empty());
|
||||
ASSERT_FALSE(other.empty());
|
||||
|
||||
ASSERT_EQ(other.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<void>());
|
||||
ASSERT_EQ(other.index(entity[0u]), 0u);
|
||||
|
||||
entt::storage<entt::entity> extended{std::move(other), std::allocator<entt::entity>{}};
|
||||
@@ -54,7 +54,7 @@ TEST(StorageEntity, Move) {
|
||||
ASSERT_TRUE(other.empty());
|
||||
ASSERT_FALSE(extended.empty());
|
||||
|
||||
ASSERT_EQ(extended.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(extended.info(), entt::type_id<void>());
|
||||
ASSERT_EQ(extended.index(entity[0u]), 0u);
|
||||
|
||||
pool = std::move(extended);
|
||||
@@ -64,7 +64,7 @@ TEST(StorageEntity, Move) {
|
||||
ASSERT_TRUE(other.empty());
|
||||
ASSERT_TRUE(extended.empty());
|
||||
|
||||
ASSERT_EQ(pool.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<void>());
|
||||
ASSERT_EQ(pool.index(entity[0u]), 0u);
|
||||
|
||||
other = entt::storage<entt::entity>{};
|
||||
@@ -75,36 +75,45 @@ TEST(StorageEntity, Move) {
|
||||
ASSERT_FALSE(pool.empty());
|
||||
ASSERT_FALSE(other.empty());
|
||||
|
||||
ASSERT_EQ(other.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<void>());
|
||||
ASSERT_EQ(other.index(entity[0u]), 0u);
|
||||
}
|
||||
|
||||
TEST(StorageEntity, Swap) {
|
||||
using traits_type = entt::entt_traits<entt::entity>;
|
||||
|
||||
entt::storage<entt::entity> pool;
|
||||
entt::storage<entt::entity> other;
|
||||
|
||||
ASSERT_EQ(pool.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(other.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<void>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<void>());
|
||||
|
||||
pool.generate(entt::entity{4});
|
||||
pool.generate(entt::entity{2});
|
||||
|
||||
other.generate(entt::entity{2});
|
||||
other.generate(entt::entity{1});
|
||||
other.erase(entt::entity{2});
|
||||
other.generate(entt::entity{0});
|
||||
other.erase(entt::entity{1});
|
||||
|
||||
ASSERT_EQ(pool.size(), 1u);
|
||||
ASSERT_EQ(other.size(), 2u);
|
||||
|
||||
pool.start_from(entt::entity{3});
|
||||
other.start_from(entt::entity{2});
|
||||
|
||||
pool.swap(other);
|
||||
|
||||
ASSERT_EQ(pool.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(other.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<void>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<void>());
|
||||
|
||||
ASSERT_EQ(pool.size(), 2u);
|
||||
ASSERT_EQ(other.size(), 1u);
|
||||
|
||||
ASSERT_EQ(pool.index(entt::entity{1}), 0u);
|
||||
ASSERT_EQ(other.index(entt::entity{4}), 0u);
|
||||
ASSERT_EQ(pool.generate(), traits_type::construct(1, 1));
|
||||
ASSERT_EQ(pool.generate(), entt::entity{2});
|
||||
ASSERT_EQ(other.generate(), entt::entity{3});
|
||||
|
||||
ASSERT_EQ(pool.index(entt::entity{0}), 0u);
|
||||
ASSERT_EQ(other.index(entt::entity{2}), 0u);
|
||||
}
|
||||
|
||||
TEST(StorageEntity, Getters) {
|
||||
@@ -143,10 +152,10 @@ TEST(StorageEntity, Generate) {
|
||||
entt::storage<entt::entity> pool;
|
||||
std::array<entt::entity, 2u> entity{};
|
||||
|
||||
ASSERT_EQ(pool.emplace(), entt::entity{0});
|
||||
ASSERT_EQ(pool.generate(), entt::entity{0});
|
||||
ASSERT_EQ(pool.generate(entt::null), entt::entity{1});
|
||||
ASSERT_EQ(pool.generate(entt::tombstone), entt::entity{2});
|
||||
ASSERT_EQ(pool.emplace(entt::entity{0}), entt::entity{3});
|
||||
ASSERT_EQ(pool.generate(entt::entity{0}), entt::entity{3});
|
||||
ASSERT_EQ(pool.generate(traits_type::construct(1, 1)), entt::entity{4});
|
||||
ASSERT_EQ(pool.generate(traits_type::construct(6, 3)), traits_type::construct(6, 3));
|
||||
|
||||
@@ -172,6 +181,35 @@ TEST(StorageEntity, Generate) {
|
||||
ASSERT_EQ(entity[1u], entt::entity{8});
|
||||
}
|
||||
|
||||
TEST(StorageEntity, GenerateRange) {
|
||||
entt::storage<entt::entity> pool;
|
||||
std::array<entt::entity, 2u> entity{};
|
||||
|
||||
pool.generate(entity.begin(), entity.end());
|
||||
|
||||
ASSERT_TRUE(pool.contains(entity[0u]));
|
||||
ASSERT_TRUE(pool.contains(entity[1u]));
|
||||
|
||||
ASSERT_FALSE(pool.empty());
|
||||
ASSERT_EQ(pool.size(), 2u);
|
||||
ASSERT_EQ(pool.free_list(), 2u);
|
||||
|
||||
pool.erase(entity.begin(), entity.end());
|
||||
|
||||
ASSERT_FALSE(pool.empty());
|
||||
ASSERT_EQ(pool.size(), 2u);
|
||||
ASSERT_EQ(pool.free_list(), 0u);
|
||||
|
||||
pool.generate(entity.begin(), entity.begin() + 1u);
|
||||
|
||||
ASSERT_TRUE(pool.contains(entity[0u]));
|
||||
ASSERT_FALSE(pool.contains(entity[1u]));
|
||||
|
||||
ASSERT_FALSE(pool.empty());
|
||||
ASSERT_EQ(pool.size(), 2u);
|
||||
ASSERT_EQ(pool.free_list(), 1u);
|
||||
}
|
||||
|
||||
TEST(StorageEntity, GenerateFrom) {
|
||||
entt::storage<entt::entity> pool;
|
||||
std::array entity{entt::entity{0}, entt::entity{1}, entt::entity{2}};
|
||||
@@ -287,35 +325,6 @@ ENTT_DEBUG_TEST(StorageEntityDeathTest, Patch) {
|
||||
ASSERT_DEATH(pool.patch(entt::null), "");
|
||||
}
|
||||
|
||||
TEST(StorageEntity, Insert) {
|
||||
entt::storage<entt::entity> pool;
|
||||
std::array<entt::entity, 2u> entity{};
|
||||
|
||||
pool.insert(entity.begin(), entity.end());
|
||||
|
||||
ASSERT_TRUE(pool.contains(entity[0u]));
|
||||
ASSERT_TRUE(pool.contains(entity[1u]));
|
||||
|
||||
ASSERT_FALSE(pool.empty());
|
||||
ASSERT_EQ(pool.size(), 2u);
|
||||
ASSERT_EQ(pool.free_list(), 2u);
|
||||
|
||||
pool.erase(entity.begin(), entity.end());
|
||||
|
||||
ASSERT_FALSE(pool.empty());
|
||||
ASSERT_EQ(pool.size(), 2u);
|
||||
ASSERT_EQ(pool.free_list(), 0u);
|
||||
|
||||
pool.generate(entity.begin(), entity.begin() + 1u);
|
||||
|
||||
ASSERT_TRUE(pool.contains(entity[0u]));
|
||||
ASSERT_FALSE(pool.contains(entity[1u]));
|
||||
|
||||
ASSERT_FALSE(pool.empty());
|
||||
ASSERT_EQ(pool.size(), 2u);
|
||||
ASSERT_EQ(pool.free_list(), 1u);
|
||||
}
|
||||
|
||||
TEST(StorageEntity, Pack) {
|
||||
entt::storage<entt::entity> pool;
|
||||
std::array entity{entt::entity{1}, entt::entity{3}, entt::entity{4}, entt::entity{2}};
|
||||
|
||||
@@ -22,21 +22,12 @@ struct StorageNoInstance: testing::Test {
|
||||
using type = Type;
|
||||
|
||||
static auto emplace_instance(entt::storage<type> &pool, const entt::entity entt) {
|
||||
if constexpr(std::is_void_v<type>) {
|
||||
return pool.emplace(entt);
|
||||
} else {
|
||||
return pool.emplace(entt, type{});
|
||||
}
|
||||
return pool.emplace(entt);
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
static auto insert_instance(entt::storage<type> &pool, const It from, const It to) {
|
||||
if constexpr(std::is_void_v<type>) {
|
||||
return pool.insert(from, to);
|
||||
} else {
|
||||
const std::array<type, 2u> value{};
|
||||
return pool.insert(from, to, value.begin());
|
||||
}
|
||||
return pool.insert(from, to);
|
||||
}
|
||||
|
||||
static auto push_instance(entt::storage<type> &pool, const entt::entity entt) {
|
||||
@@ -64,13 +55,13 @@ TYPED_TEST(StorageNoInstance, Constructors) {
|
||||
|
||||
ASSERT_EQ(pool.policy(), entt::deletion_policy::swap_and_pop);
|
||||
ASSERT_NO_THROW([[maybe_unused]] auto alloc = pool.get_allocator());
|
||||
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<value_type>());
|
||||
|
||||
pool = entt::storage<value_type>{std::allocator<value_type>{}};
|
||||
|
||||
ASSERT_EQ(pool.policy(), entt::deletion_policy::swap_and_pop);
|
||||
ASSERT_NO_THROW([[maybe_unused]] auto alloc = pool.get_allocator());
|
||||
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<value_type>());
|
||||
}
|
||||
|
||||
TYPED_TEST(StorageNoInstance, Move) {
|
||||
@@ -91,7 +82,7 @@ TYPED_TEST(StorageNoInstance, Move) {
|
||||
ASSERT_TRUE(pool.empty());
|
||||
ASSERT_FALSE(other.empty());
|
||||
|
||||
ASSERT_EQ(other.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.index(entity[0u]), 0u);
|
||||
|
||||
entt::storage<value_type> extended{std::move(other), std::allocator<value_type>{}};
|
||||
@@ -101,7 +92,7 @@ TYPED_TEST(StorageNoInstance, Move) {
|
||||
ASSERT_TRUE(other.empty());
|
||||
ASSERT_FALSE(extended.empty());
|
||||
|
||||
ASSERT_EQ(extended.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(extended.info(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(extended.index(entity[0u]), 0u);
|
||||
|
||||
pool = std::move(extended);
|
||||
@@ -111,18 +102,18 @@ TYPED_TEST(StorageNoInstance, Move) {
|
||||
ASSERT_TRUE(other.empty());
|
||||
ASSERT_TRUE(extended.empty());
|
||||
|
||||
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.index(entity[0u]), 0u);
|
||||
|
||||
other = entt::storage<value_type>{};
|
||||
other.emplace(entity[1u], 2);
|
||||
other.emplace(entity[1u]);
|
||||
other = std::move(pool);
|
||||
test::is_initialized(pool);
|
||||
|
||||
ASSERT_FALSE(pool.empty());
|
||||
ASSERT_FALSE(other.empty());
|
||||
|
||||
ASSERT_EQ(other.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.index(entity[0u]), 0u);
|
||||
}
|
||||
|
||||
@@ -132,8 +123,8 @@ TYPED_TEST(StorageNoInstance, Swap) {
|
||||
entt::storage<value_type> pool;
|
||||
entt::storage<value_type> other;
|
||||
|
||||
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<value_type>());
|
||||
|
||||
pool.emplace(entt::entity{4});
|
||||
|
||||
@@ -146,8 +137,8 @@ TYPED_TEST(StorageNoInstance, Swap) {
|
||||
|
||||
pool.swap(other);
|
||||
|
||||
ASSERT_EQ(pool.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.type(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(pool.info(), entt::type_id<value_type>());
|
||||
ASSERT_EQ(other.info(), entt::type_id<value_type>());
|
||||
|
||||
ASSERT_EQ(pool.size(), 1u);
|
||||
ASSERT_EQ(other.size(), 1u);
|
||||
@@ -162,7 +153,7 @@ TYPED_TEST(StorageNoInstance, Getters) {
|
||||
entt::storage<value_type> pool;
|
||||
const entt::entity entity{4};
|
||||
|
||||
pool.emplace(entity, 3);
|
||||
pool.emplace(entity);
|
||||
|
||||
testing::StaticAssertTypeEq<decltype(pool.get({})), void>();
|
||||
testing::StaticAssertTypeEq<decltype(std::as_const(pool).get({})), void>();
|
||||
|
||||
@@ -855,19 +855,19 @@ TEST(MultiStorageView, UseAndRefresh) {
|
||||
|
||||
view.use<int>();
|
||||
|
||||
ASSERT_EQ(view.handle()->type(), entt::type_id<int>());
|
||||
ASSERT_EQ(view.handle()->info(), entt::type_id<int>());
|
||||
ASSERT_EQ(view.front(), entity[1u]);
|
||||
ASSERT_EQ(view.back(), entity[0u]);
|
||||
|
||||
view.use<char>();
|
||||
|
||||
ASSERT_EQ(view.handle()->type(), entt::type_id<char>());
|
||||
ASSERT_EQ(view.handle()->info(), entt::type_id<char>());
|
||||
ASSERT_EQ(view.front(), entity[0u]);
|
||||
ASSERT_EQ(view.back(), entity[1u]);
|
||||
|
||||
view.refresh();
|
||||
|
||||
ASSERT_EQ(view.handle()->type(), entt::type_id<int>());
|
||||
ASSERT_EQ(view.handle()->info(), entt::type_id<int>());
|
||||
}
|
||||
|
||||
TEST(MultiStorageView, Each) {
|
||||
|
||||
@@ -11,7 +11,6 @@ _TESTS = [
|
||||
"meta_ctor",
|
||||
"meta_custom",
|
||||
"meta_data",
|
||||
"meta_dtor",
|
||||
"meta_factory",
|
||||
"meta_func",
|
||||
"meta_handle",
|
||||
|
||||
+42
-35
@@ -5,7 +5,10 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/core/any.hpp>
|
||||
#include <entt/core/hashed_string.hpp>
|
||||
#include <entt/locator/locator.hpp>
|
||||
#include <entt/meta/context.hpp>
|
||||
#include <entt/meta/factory.hpp>
|
||||
#include <entt/meta/meta.hpp>
|
||||
#include <entt/meta/resolve.hpp>
|
||||
@@ -36,15 +39,10 @@ struct empty {
|
||||
empty &operator=(const empty &) = default;
|
||||
|
||||
virtual ~empty() {
|
||||
++destructor_counter;
|
||||
++counter;
|
||||
}
|
||||
|
||||
static void destroy(empty &) {
|
||||
++destroy_counter;
|
||||
}
|
||||
|
||||
inline static int destroy_counter = 0; // NOLINT
|
||||
inline static int destructor_counter = 0; // NOLINT
|
||||
inline static int counter = 0; // NOLINT
|
||||
};
|
||||
|
||||
struct fat: empty {
|
||||
@@ -91,13 +89,11 @@ struct MetaAny: ::testing::Test {
|
||||
using namespace entt::literals;
|
||||
|
||||
entt::meta_factory<empty>{}
|
||||
.type("empty"_hs)
|
||||
.dtor<empty::destroy>();
|
||||
.type("empty"_hs);
|
||||
|
||||
entt::meta_factory<fat>{}
|
||||
.type("fat"_hs)
|
||||
.base<empty>()
|
||||
.dtor<fat::destroy>();
|
||||
.base<empty>();
|
||||
|
||||
entt::meta_factory<clazz>{}
|
||||
.type("clazz"_hs)
|
||||
@@ -106,8 +102,7 @@ struct MetaAny: ::testing::Test {
|
||||
.func<clazz::func>("func"_hs)
|
||||
.conv<int>();
|
||||
|
||||
empty::destroy_counter = 0;
|
||||
empty::destructor_counter = 0;
|
||||
empty::counter = 0;
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
@@ -154,10 +149,10 @@ TEST_F(MetaAny, SBO) {
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(any.base().owner());
|
||||
ASSERT_EQ(any.policy(), entt::meta_any_policy::embedded);
|
||||
ASSERT_EQ(any.base().policy(), entt::any_policy::embedded);
|
||||
ASSERT_FALSE(any.try_cast<std::size_t>());
|
||||
ASSERT_EQ(any.cast<char>(), 'c');
|
||||
ASSERT_NE(any.data(), nullptr);
|
||||
ASSERT_NE(any.base().data(), nullptr);
|
||||
ASSERT_EQ(any, entt::meta_any{'c'});
|
||||
ASSERT_NE(entt::meta_any{'h'}, any);
|
||||
}
|
||||
@@ -168,10 +163,10 @@ TEST_F(MetaAny, NoSBO) {
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(any.base().owner());
|
||||
ASSERT_EQ(any.policy(), entt::meta_any_policy::dynamic);
|
||||
ASSERT_EQ(any.base().policy(), entt::any_policy::dynamic);
|
||||
ASSERT_FALSE(any.try_cast<std::size_t>());
|
||||
ASSERT_EQ(any.cast<fat>(), instance);
|
||||
ASSERT_NE(any.data(), nullptr);
|
||||
ASSERT_NE(any.base().data(), nullptr);
|
||||
ASSERT_EQ(any, entt::meta_any{instance});
|
||||
ASSERT_NE(any, fat{});
|
||||
}
|
||||
@@ -194,7 +189,7 @@ TEST_F(MetaAny, SBOInPlaceConstruction) {
|
||||
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_FALSE(other.base().owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::ref);
|
||||
ASSERT_EQ(other.base().policy(), entt::any_policy::ref);
|
||||
ASSERT_FALSE(other.try_cast<std::size_t>());
|
||||
ASSERT_EQ(other.cast<int>(), 2);
|
||||
ASSERT_NE(other.base().data(), nullptr);
|
||||
@@ -371,15 +366,17 @@ TEST_F(MetaAny, SBOMoveAssignment) {
|
||||
ASSERT_NE(other, entt::meta_any{0});
|
||||
}
|
||||
|
||||
TEST_F(MetaAnyDeathTest, SBOSelfMoveAssignment) {
|
||||
TEST_F(MetaAny, SBOSelfMoveAssignment) {
|
||||
entt::meta_any any{3};
|
||||
|
||||
// avoid warnings due to self-assignment
|
||||
any = std::move(*&any);
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(any.type());
|
||||
ASSERT_EQ(any.base().data(), nullptr);
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_FALSE(any.try_cast<std::size_t>());
|
||||
ASSERT_EQ(any.cast<int>(), 3);
|
||||
ASSERT_EQ(any, entt::meta_any{3});
|
||||
ASSERT_NE(any, entt::meta_any{0});
|
||||
}
|
||||
|
||||
TEST_F(MetaAny, SBODirectAssignment) {
|
||||
@@ -526,7 +523,7 @@ TEST_F(MetaAny, NoSBOInPlaceConstruction) {
|
||||
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_FALSE(other.base().owner());
|
||||
ASSERT_EQ(other.policy(), entt::any_policy::ref);
|
||||
ASSERT_EQ(other.base().policy(), entt::any_policy::ref);
|
||||
ASSERT_FALSE(other.try_cast<std::size_t>());
|
||||
ASSERT_EQ(other.cast<fat>(), instance);
|
||||
ASSERT_NE(other.base().data(), nullptr);
|
||||
@@ -705,16 +702,18 @@ TEST_F(MetaAny, NoSBOMoveAssignment) {
|
||||
ASSERT_NE(other, fat{});
|
||||
}
|
||||
|
||||
TEST_F(MetaAnyDeathTest, NoSBOSelfMoveAssignment) {
|
||||
TEST_F(MetaAny, NoSBOSelfMoveAssignment) {
|
||||
const fat instance{.1, .2, .3, .4};
|
||||
entt::meta_any any{instance};
|
||||
|
||||
// avoid warnings due to self-assignment
|
||||
any = std::move(*&any);
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(any.type());
|
||||
ASSERT_EQ(any.base().data(), nullptr);
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_FALSE(any.try_cast<std::size_t>());
|
||||
ASSERT_EQ(any.cast<fat>(), instance);
|
||||
ASSERT_EQ(any, entt::meta_any{instance});
|
||||
ASSERT_NE(any, fat{});
|
||||
}
|
||||
|
||||
TEST_F(MetaAny, NoSBODirectAssignment) {
|
||||
@@ -961,15 +960,16 @@ TEST_F(MetaAny, VoidMoveAssignment) {
|
||||
ASSERT_EQ(other, entt::meta_any{std::in_place_type<void>});
|
||||
}
|
||||
|
||||
TEST_F(MetaAnyDeathTest, VoidSelfMoveAssignment) {
|
||||
TEST_F(MetaAny, VoidSelfMoveAssignment) {
|
||||
entt::meta_any any{std::in_place_type<void>};
|
||||
|
||||
// avoid warnings due to self-assignment
|
||||
any = std::move(*&any);
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(any.type());
|
||||
ASSERT_EQ(any.base().data(), nullptr);
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_FALSE(any.try_cast<std::size_t>());
|
||||
ASSERT_EQ(any.type(), entt::resolve<void>());
|
||||
ASSERT_EQ(any, entt::meta_any{std::in_place_type<void>});
|
||||
}
|
||||
|
||||
TEST_F(MetaAny, SBOMoveInvalidate) {
|
||||
@@ -1021,8 +1021,7 @@ TEST_F(MetaAny, SBODestruction) {
|
||||
any = std::move(other);
|
||||
}
|
||||
|
||||
ASSERT_EQ(empty::destroy_counter, 3);
|
||||
ASSERT_EQ(empty::destructor_counter, 6);
|
||||
ASSERT_EQ(empty::counter, 6);
|
||||
}
|
||||
|
||||
TEST_F(MetaAny, NoSBODestruction) {
|
||||
@@ -1034,8 +1033,7 @@ TEST_F(MetaAny, NoSBODestruction) {
|
||||
any = std::move(other);
|
||||
}
|
||||
|
||||
ASSERT_EQ(fat::destroy_counter, 3);
|
||||
ASSERT_EQ(empty::destructor_counter, 4);
|
||||
ASSERT_EQ(empty::counter, 4);
|
||||
}
|
||||
|
||||
TEST_F(MetaAny, VoidDestruction) {
|
||||
@@ -1328,16 +1326,21 @@ TEST_F(MetaAny, Cast) {
|
||||
}
|
||||
|
||||
TEST_F(MetaAny, AllowCast) {
|
||||
entt::meta_any any{};
|
||||
entt::meta_any instance{clazz{}};
|
||||
entt::meta_any other{fat{}};
|
||||
entt::meta_any arithmetic{3};
|
||||
auto as_cref = entt::forward_as_meta(arithmetic.cast<const int &>());
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_TRUE(instance);
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_TRUE(arithmetic);
|
||||
ASSERT_TRUE(as_cref);
|
||||
|
||||
ASSERT_FALSE(any.allow_cast<clazz>());
|
||||
ASSERT_FALSE(std::as_const(any).allow_cast<clazz>());
|
||||
|
||||
ASSERT_TRUE(instance.allow_cast<clazz>());
|
||||
ASSERT_TRUE(instance.allow_cast<clazz &>());
|
||||
ASSERT_TRUE(instance.allow_cast<const clazz &>());
|
||||
@@ -1386,16 +1389,20 @@ TEST_F(MetaAny, AllowCast) {
|
||||
}
|
||||
|
||||
TEST_F(MetaAny, OpaqueAllowCast) {
|
||||
entt::meta_any any{};
|
||||
entt::meta_any instance{clazz{}};
|
||||
entt::meta_any other{fat{}};
|
||||
entt::meta_any arithmetic{3};
|
||||
auto as_cref = entt::forward_as_meta(arithmetic.cast<const int &>());
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_TRUE(instance);
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_TRUE(arithmetic);
|
||||
ASSERT_TRUE(as_cref);
|
||||
|
||||
ASSERT_FALSE(any.allow_cast(entt::resolve<clazz>()));
|
||||
|
||||
ASSERT_TRUE(instance.allow_cast(entt::resolve<clazz>()));
|
||||
ASSERT_EQ(instance.type(), entt::resolve<clazz>());
|
||||
|
||||
|
||||
@@ -117,7 +117,6 @@ class MetaContext: public ::testing::Test {
|
||||
.custom<char>('c')
|
||||
.base<base>()
|
||||
.ctor<char, int>()
|
||||
.dtor<&clazz::move_to_bucket>()
|
||||
.data<nullptr, &clazz::value>("value"_hs)
|
||||
.data<&clazz::value>("rw"_hs)
|
||||
.func<&clazz::cfunc>("func"_hs);
|
||||
@@ -336,21 +335,6 @@ TEST_F(MetaContext, MetaConv) {
|
||||
ASSERT_EQ(local.cast<int>(), value.get_mul());
|
||||
}
|
||||
|
||||
TEST_F(MetaContext, MetaDtor) {
|
||||
auto global = entt::resolve<clazz>().construct();
|
||||
auto local = entt::resolve<clazz>(ctx()).construct();
|
||||
|
||||
ASSERT_EQ(clazz::bucket, bucket_value);
|
||||
|
||||
global.reset();
|
||||
|
||||
ASSERT_EQ(clazz::bucket, bucket_value);
|
||||
|
||||
local.reset();
|
||||
|
||||
ASSERT_NE(clazz::bucket, bucket_value);
|
||||
}
|
||||
|
||||
TEST_F(MetaContext, MetaCustom) {
|
||||
using namespace entt::literals;
|
||||
|
||||
@@ -480,19 +464,12 @@ TEST_F(MetaContext, MetaHandle) {
|
||||
|
||||
entt::meta_handle global{value};
|
||||
entt::meta_handle ctx_value{ctx(), value};
|
||||
entt::meta_handle two_step_local{entt::meta_ctx_arg, ctx()};
|
||||
|
||||
ASSERT_TRUE(global);
|
||||
ASSERT_TRUE(ctx_value);
|
||||
ASSERT_FALSE(two_step_local);
|
||||
|
||||
two_step_local->emplace<int &>(value);
|
||||
|
||||
ASSERT_TRUE(two_step_local);
|
||||
|
||||
ASSERT_EQ(global->type().data("marker"_hs).get({}).cast<int>(), global_marker);
|
||||
ASSERT_EQ(ctx_value->type().data("marker"_hs).get({}).cast<int>(), local_marker);
|
||||
ASSERT_EQ(two_step_local->type().data("marker"_hs).get({}).cast<int>(), local_marker);
|
||||
}
|
||||
|
||||
TEST_F(MetaContext, ForwardAsMeta) {
|
||||
|
||||
@@ -17,18 +17,10 @@
|
||||
|
||||
struct base {
|
||||
virtual ~base() = default;
|
||||
|
||||
static void destroy(base &) {
|
||||
++counter;
|
||||
}
|
||||
|
||||
inline static int counter = 0; // NOLINT
|
||||
int value{3};
|
||||
};
|
||||
|
||||
struct derived: base {
|
||||
derived() = default;
|
||||
};
|
||||
struct derived: base {};
|
||||
|
||||
struct clazz {
|
||||
operator int() const {
|
||||
@@ -70,18 +62,6 @@ struct setter_getter {
|
||||
int value{0};
|
||||
};
|
||||
|
||||
struct multi_setter {
|
||||
void from_double(double val) {
|
||||
value = static_cast<int>(val);
|
||||
}
|
||||
|
||||
void from_string(const char *val) {
|
||||
value = std::atoi(val);
|
||||
}
|
||||
|
||||
int value{0};
|
||||
};
|
||||
|
||||
struct array {
|
||||
inline static int global[2]; // NOLINT
|
||||
int local[4]; // NOLINT
|
||||
@@ -93,13 +73,11 @@ struct MetaData: ::testing::Test {
|
||||
|
||||
entt::meta_factory<base>{}
|
||||
.type("base"_hs)
|
||||
.dtor<base::destroy>()
|
||||
.data<&base::value>("value"_hs);
|
||||
|
||||
entt::meta_factory<derived>{}
|
||||
.type("derived"_hs)
|
||||
.base<base>()
|
||||
.dtor<derived::destroy>()
|
||||
.data<&base::value>("value_from_base"_hs);
|
||||
|
||||
entt::meta_factory<clazz>{}
|
||||
@@ -108,9 +86,9 @@ struct MetaData: ::testing::Test {
|
||||
.custom<char>('c')
|
||||
.traits(test::meta_traits::one | test::meta_traits::two | test::meta_traits::three)
|
||||
.data<&clazz::i, entt::as_cref_t>("ci"_hs)
|
||||
.data<&clazz::j>("j"_hs)
|
||||
.data<&clazz::j>("j")
|
||||
.traits(test::meta_traits::one)
|
||||
.data<&clazz::h>("h"_hs)
|
||||
.data<&clazz::h>("h"_hs, "hhh")
|
||||
.traits(test::meta_traits::two)
|
||||
.data<&clazz::k>("k"_hs)
|
||||
.traits(test::meta_traits::three)
|
||||
@@ -119,25 +97,26 @@ struct MetaData: ::testing::Test {
|
||||
.data<&clazz::i, entt::as_void_t>("void"_hs)
|
||||
.conv<int>();
|
||||
|
||||
entt::meta_factory<clazz>{}
|
||||
.data<&clazz::i, entt::as_is_t>("ir"_hs)
|
||||
.data<&clazz::j, entt::as_is_t>("jc"_hs)
|
||||
.data<&clazz::h, entt::as_is_t>("hr"_hs)
|
||||
.data<&clazz::k, entt::as_is_t>("kc"_hs)
|
||||
.data<nullptr, &clazz::operator int, entt::as_is_t>("ov"_hs);
|
||||
|
||||
entt::meta_factory<setter_getter>{}
|
||||
.type("setter_getter"_hs)
|
||||
.data<&setter_getter::static_setter, &setter_getter::static_getter>("x"_hs)
|
||||
.data<&setter_getter::setter, &setter_getter::getter>("y"_hs)
|
||||
.data<&setter_getter::static_setter, &setter_getter::getter>("z"_hs)
|
||||
.data<&setter_getter::setter_with_ref, &setter_getter::getter_with_ref>("w"_hs)
|
||||
.data<nullptr, &setter_getter::getter>("z_ro"_hs)
|
||||
.data<&setter_getter::setter_with_ref, &setter_getter::getter_with_ref>("w")
|
||||
.data<nullptr, &setter_getter::getter>("z_ro"_hs, "readonly")
|
||||
.data<nullptr, &setter_getter::value>("value"_hs);
|
||||
|
||||
entt::meta_factory<multi_setter>{}
|
||||
.type("multi_setter"_hs)
|
||||
.data<entt::value_list<&multi_setter::from_double, &multi_setter::from_string>, &multi_setter::value>("value"_hs);
|
||||
|
||||
entt::meta_factory<array>{}
|
||||
.type("array"_hs)
|
||||
.data<&array::global>("global"_hs)
|
||||
.data<&array::local>("local"_hs);
|
||||
|
||||
base::counter = 0;
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
@@ -147,22 +126,6 @@ struct MetaData: ::testing::Test {
|
||||
|
||||
using MetaDataDeathTest = MetaData;
|
||||
|
||||
TEST_F(MetaData, SafeWhenEmpty) {
|
||||
const entt::meta_data data{};
|
||||
|
||||
ASSERT_FALSE(data);
|
||||
ASSERT_EQ(data, entt::meta_data{});
|
||||
ASSERT_EQ(data.arity(), 0u);
|
||||
ASSERT_FALSE(data.is_const());
|
||||
ASSERT_FALSE(data.is_static());
|
||||
ASSERT_EQ(data.type(), entt::meta_type{});
|
||||
ASSERT_FALSE(data.set({}, 0));
|
||||
ASSERT_FALSE(data.get({}));
|
||||
ASSERT_EQ(data.arg(0u), entt::meta_type{});
|
||||
ASSERT_EQ(data.traits<test::meta_traits>(), test::meta_traits::none);
|
||||
ASSERT_EQ(static_cast<const char *>(data.custom()), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(MetaData, UserTraits) {
|
||||
using namespace entt::literals;
|
||||
|
||||
@@ -200,6 +163,21 @@ ENTT_DEBUG_TEST_F(MetaDataDeathTest, Custom) {
|
||||
ASSERT_DEATH([[maybe_unused]] const char value = entt::resolve<clazz>().data("j"_hs).custom(), "");
|
||||
}
|
||||
|
||||
TEST_F(MetaData, Name) {
|
||||
using namespace entt::literals;
|
||||
|
||||
const entt::meta_type type = entt::resolve<clazz>();
|
||||
const entt::meta_type other = entt::resolve<setter_getter>();
|
||||
|
||||
ASSERT_EQ(type.data("i"_hs).name(), nullptr);
|
||||
ASSERT_STREQ(type.data("j"_hs).name(), "j");
|
||||
ASSERT_STREQ(type.data("h"_hs).name(), "hhh");
|
||||
|
||||
ASSERT_EQ(other.data("z"_hs).name(), nullptr);
|
||||
ASSERT_STREQ(other.data("w"_hs).name(), "w");
|
||||
ASSERT_STREQ(other.data("z_ro"_hs).name(), "readonly");
|
||||
}
|
||||
|
||||
TEST_F(MetaData, Comparison) {
|
||||
using namespace entt::literals;
|
||||
|
||||
@@ -335,10 +313,13 @@ TEST_F(MetaData, SetCast) {
|
||||
using namespace entt::literals;
|
||||
|
||||
clazz instance{};
|
||||
derived other{};
|
||||
|
||||
ASSERT_EQ(base::counter, 0);
|
||||
ASSERT_TRUE(entt::resolve<clazz>().data("base"_hs).set(instance, derived{}));
|
||||
ASSERT_EQ(base::counter, 1);
|
||||
other.value = 1;
|
||||
|
||||
ASSERT_EQ(instance.instance.value, 3);
|
||||
ASSERT_TRUE(entt::resolve<clazz>().data("base"_hs).set(instance, other));
|
||||
ASSERT_EQ(instance.instance.value, 1);
|
||||
}
|
||||
|
||||
TEST_F(MetaData, SetConvert) {
|
||||
@@ -490,30 +471,6 @@ TEST_F(MetaData, SetterGetterReadOnlyDataMember) {
|
||||
ASSERT_EQ(data.get(instance).cast<int>(), 0);
|
||||
}
|
||||
|
||||
TEST_F(MetaData, MultiSetter) {
|
||||
using namespace entt::literals;
|
||||
|
||||
auto data = entt::resolve<multi_setter>().data("value"_hs);
|
||||
multi_setter instance{};
|
||||
|
||||
ASSERT_TRUE(data);
|
||||
ASSERT_EQ(data.arity(), 2u);
|
||||
ASSERT_EQ(data.type(), entt::resolve<int>());
|
||||
ASSERT_EQ(data.arg(0u), entt::resolve<double>());
|
||||
ASSERT_EQ(data.arg(1u), entt::resolve<const char *>());
|
||||
ASSERT_EQ(data.arg(2u), entt::meta_type{});
|
||||
ASSERT_FALSE(data.is_const());
|
||||
ASSERT_FALSE(data.is_static());
|
||||
ASSERT_EQ(data.get(instance).cast<int>(), 0);
|
||||
ASSERT_TRUE(data.set(instance, 1));
|
||||
ASSERT_EQ(data.get(instance).cast<int>(), 1);
|
||||
ASSERT_TRUE(data.set(instance, 2.));
|
||||
ASSERT_EQ(data.get(instance).cast<int>(), 2);
|
||||
ASSERT_FALSE(data.set(instance, std::string{"3"}));
|
||||
ASSERT_TRUE(data.set(instance, std::string{"3"}.c_str()));
|
||||
ASSERT_EQ(data.get(instance).cast<int>(), 3);
|
||||
}
|
||||
|
||||
TEST_F(MetaData, ConstInstance) {
|
||||
using namespace entt::literals;
|
||||
|
||||
@@ -591,6 +548,44 @@ TEST_F(MetaData, AsVoid) {
|
||||
ASSERT_EQ(data.get(instance), entt::meta_any{std::in_place_type<void>});
|
||||
}
|
||||
|
||||
TEST_F(MetaData, AsIs) {
|
||||
using namespace entt::literals;
|
||||
|
||||
auto type = entt::resolve<clazz>();
|
||||
entt::meta_data data{};
|
||||
clazz instance{};
|
||||
|
||||
data = type.data("ir"_hs);
|
||||
|
||||
ASSERT_TRUE(data);
|
||||
ASSERT_EQ(data.type(), entt::resolve<int>());
|
||||
ASSERT_EQ(data.get(instance).base().policy(), entt::any_policy::ref);
|
||||
|
||||
data = type.data("jc"_hs);
|
||||
|
||||
ASSERT_TRUE(data);
|
||||
ASSERT_EQ(data.type(), entt::resolve<int>());
|
||||
ASSERT_EQ(data.get(instance).base().policy(), entt::any_policy::cref);
|
||||
|
||||
data = type.data("hr"_hs);
|
||||
|
||||
ASSERT_TRUE(data);
|
||||
ASSERT_EQ(data.type(), entt::resolve<int>());
|
||||
ASSERT_EQ(data.get(instance).base().policy(), entt::any_policy::ref);
|
||||
|
||||
data = type.data("kc"_hs);
|
||||
|
||||
ASSERT_TRUE(data);
|
||||
ASSERT_EQ(data.type(), entt::resolve<int>());
|
||||
ASSERT_EQ(data.get(instance).base().policy(), entt::any_policy::cref);
|
||||
|
||||
data = type.data("ov"_hs);
|
||||
|
||||
ASSERT_TRUE(data);
|
||||
ASSERT_EQ(data.type(), entt::resolve<int>());
|
||||
ASSERT_EQ(data.get(instance).base().policy(), entt::any_policy::embedded);
|
||||
}
|
||||
|
||||
TEST_F(MetaData, AsRef) {
|
||||
using namespace entt::literals;
|
||||
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
#include <utility>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/core/hashed_string.hpp>
|
||||
#include <entt/locator/locator.hpp>
|
||||
#include <entt/meta/context.hpp>
|
||||
#include <entt/meta/factory.hpp>
|
||||
#include <entt/meta/meta.hpp>
|
||||
#include <entt/meta/node.hpp>
|
||||
#include <entt/meta/resolve.hpp>
|
||||
|
||||
struct clazz {
|
||||
clazz(int &cnt)
|
||||
: counter{&cnt} {
|
||||
++(*counter);
|
||||
}
|
||||
|
||||
static void destroy_decr(clazz &instance) {
|
||||
--(*instance.counter);
|
||||
}
|
||||
|
||||
void destroy_incr() const {
|
||||
++(*counter);
|
||||
}
|
||||
|
||||
int *counter;
|
||||
};
|
||||
|
||||
struct MetaDtor: ::testing::Test {
|
||||
void SetUp() override {
|
||||
using namespace entt::literals;
|
||||
|
||||
entt::meta_factory<clazz>{}
|
||||
.type("clazz"_hs)
|
||||
.ctor<int &>()
|
||||
.dtor<clazz::destroy_decr>();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
entt::meta_reset();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(MetaDtor, Dtor) {
|
||||
int counter{};
|
||||
|
||||
auto any = entt::resolve<clazz>().construct(entt::forward_as_meta(counter));
|
||||
auto cref = std::as_const(any).as_ref();
|
||||
auto ref = any.as_ref();
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(cref);
|
||||
ASSERT_TRUE(ref);
|
||||
|
||||
ASSERT_EQ(counter, 1);
|
||||
|
||||
cref.reset();
|
||||
ref.reset();
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_FALSE(cref);
|
||||
ASSERT_FALSE(ref);
|
||||
|
||||
ASSERT_EQ(counter, 1);
|
||||
|
||||
any.reset();
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(cref);
|
||||
ASSERT_FALSE(ref);
|
||||
|
||||
ASSERT_EQ(counter, 0);
|
||||
}
|
||||
|
||||
TEST_F(MetaDtor, AsRefConstruction) {
|
||||
int counter{};
|
||||
|
||||
clazz instance{counter};
|
||||
auto any = entt::forward_as_meta(instance);
|
||||
auto cany = entt::forward_as_meta(std::as_const(instance));
|
||||
auto cref = cany.as_ref();
|
||||
auto ref = any.as_ref();
|
||||
|
||||
ASSERT_TRUE(any);
|
||||
ASSERT_TRUE(cany);
|
||||
ASSERT_TRUE(cref);
|
||||
ASSERT_TRUE(ref);
|
||||
|
||||
ASSERT_EQ(counter, 1);
|
||||
|
||||
any.reset();
|
||||
cany.reset();
|
||||
cref.reset();
|
||||
ref.reset();
|
||||
|
||||
ASSERT_FALSE(any);
|
||||
ASSERT_FALSE(cany);
|
||||
ASSERT_FALSE(cref);
|
||||
ASSERT_FALSE(ref);
|
||||
|
||||
ASSERT_EQ(counter, 1);
|
||||
}
|
||||
|
||||
TEST_F(MetaDtor, ReRegistration) {
|
||||
SetUp();
|
||||
|
||||
int counter{};
|
||||
auto &&node = entt::internal::resolve<clazz>(entt::internal::meta_context::from(entt::locator<entt::meta_ctx>::value_or()));
|
||||
|
||||
ASSERT_NE(node.dtor.dtor, nullptr);
|
||||
|
||||
entt::meta_factory<clazz>{}.dtor<&clazz::destroy_incr>();
|
||||
entt::resolve<clazz>().construct(entt::forward_as_meta(counter)).reset();
|
||||
|
||||
ASSERT_EQ(counter, 2);
|
||||
}
|
||||
@@ -49,18 +49,6 @@ private:
|
||||
int value{};
|
||||
};
|
||||
|
||||
struct dtor_callback {
|
||||
dtor_callback(bool &ref)
|
||||
: cb{&ref} {}
|
||||
|
||||
static void on_destroy(dtor_callback &instance) {
|
||||
*instance.cb = !*instance.cb;
|
||||
}
|
||||
|
||||
private:
|
||||
bool *cb;
|
||||
};
|
||||
|
||||
struct MetaFactory: ::testing::Test {
|
||||
void TearDown() override {
|
||||
entt::meta_reset();
|
||||
@@ -115,7 +103,7 @@ ENTT_DEBUG_TEST_F(MetaFactoryDeathTest, Type) {
|
||||
|
||||
factory.type("foo"_hs);
|
||||
|
||||
ASSERT_DEATH(other.type("foo"_hs);, "");
|
||||
ASSERT_DEATH(other.type("foo"_hs), "");
|
||||
}
|
||||
|
||||
TEST_F(MetaFactory, Base) {
|
||||
@@ -175,22 +163,6 @@ TEST_F(MetaFactory, Ctor) {
|
||||
ASSERT_EQ(other.cast<const clazz &>().get_int(), values[1u]);
|
||||
}
|
||||
|
||||
TEST_F(MetaFactory, Dtor) {
|
||||
bool check = false;
|
||||
entt::meta_factory<dtor_callback> factory{};
|
||||
entt::meta_any any{std::in_place_type<dtor_callback>, check};
|
||||
|
||||
any.reset();
|
||||
|
||||
ASSERT_FALSE(check);
|
||||
|
||||
factory.dtor<&dtor_callback::on_destroy>();
|
||||
any.emplace<dtor_callback>(check);
|
||||
any.reset();
|
||||
|
||||
ASSERT_TRUE(check);
|
||||
}
|
||||
|
||||
TEST_F(MetaFactory, DataMemberObject) {
|
||||
using namespace entt::literals;
|
||||
|
||||
@@ -282,27 +254,6 @@ TEST_F(MetaFactory, DataSetterGetter) {
|
||||
ASSERT_TRUE(type.set("value"_hs, instance, instance.get_int()));
|
||||
}
|
||||
|
||||
TEST_F(MetaFactory, DataMultiSetterGetter) {
|
||||
using namespace entt::literals;
|
||||
|
||||
clazz instance{1};
|
||||
entt::meta_factory<clazz> factory{};
|
||||
entt::meta_type type = entt::resolve<clazz>();
|
||||
|
||||
ASSERT_FALSE(type.data("value"_hs));
|
||||
|
||||
factory.data<entt::value_list<&clazz::set_int, &clazz::set_boxed_int>, &clazz::get_int>("value"_hs);
|
||||
type = entt::resolve<clazz>();
|
||||
|
||||
ASSERT_TRUE(type.data("value"_hs));
|
||||
ASSERT_EQ(type.get("value"_hs, std::as_const(instance)), instance.get_int());
|
||||
ASSERT_EQ(type.get("value"_hs, instance), instance.get_int());
|
||||
ASSERT_FALSE(type.set("value"_hs, std::as_const(instance), instance.get_int()));
|
||||
ASSERT_TRUE(type.set("value"_hs, instance, instance.get_int()));
|
||||
ASSERT_FALSE(type.set("value"_hs, std::as_const(instance), test::boxed_int{instance.get_int()}));
|
||||
ASSERT_TRUE(type.set("value"_hs, instance, test::boxed_int{instance.get_int()}));
|
||||
}
|
||||
|
||||
TEST_F(MetaFactory, DataOverwrite) {
|
||||
using namespace entt::literals;
|
||||
|
||||
@@ -400,6 +351,23 @@ TEST_F(MetaFactory, Traits) {
|
||||
ASSERT_EQ(type.data("member"_hs).traits<test::meta_traits>(), test::meta_traits::one);
|
||||
ASSERT_EQ(type.func("func"_hs).traits<test::meta_traits>(), test::meta_traits::two);
|
||||
ASSERT_EQ(type.func("func"_hs).next().traits<test::meta_traits>(), test::meta_traits::three);
|
||||
|
||||
entt::meta_factory<clazz>{}
|
||||
.traits(test::meta_traits::one, true)
|
||||
.data<&base::member>("member"_hs)
|
||||
.traits(test::meta_traits::one | test::meta_traits::three, true)
|
||||
.func<&clazz::set_int>("func"_hs)
|
||||
.traits(test::meta_traits::one | test::meta_traits::three, true)
|
||||
.func<&clazz::set_boxed_int>("func"_hs)
|
||||
.traits(test::meta_traits::all, true);
|
||||
|
||||
// traits are copied and never refreshed
|
||||
type = entt::resolve<clazz>();
|
||||
|
||||
ASSERT_EQ(type.traits<test::meta_traits>(), test::meta_traits::three);
|
||||
ASSERT_EQ(type.data("member"_hs).traits<test::meta_traits>(), test::meta_traits::none);
|
||||
ASSERT_EQ(type.func("func"_hs).traits<test::meta_traits>(), test::meta_traits::two);
|
||||
ASSERT_EQ(type.func("func"_hs).next().traits<test::meta_traits>(), test::meta_traits::none);
|
||||
}
|
||||
|
||||
TEST_F(MetaFactory, Custom) {
|
||||
@@ -435,25 +403,6 @@ TEST_F(MetaFactory, Custom) {
|
||||
ASSERT_EQ(static_cast<int>(type.func("func"_hs).next().custom()), 3);
|
||||
}
|
||||
|
||||
TEST_F(MetaFactory, Meta) {
|
||||
entt::meta_ctx ctx{};
|
||||
|
||||
ASSERT_EQ(entt::resolve(entt::type_id<int>()), entt::meta_type{});
|
||||
ASSERT_EQ(entt::resolve(ctx, entt::type_id<int>()), entt::meta_type{});
|
||||
|
||||
auto factory = entt::meta<int>();
|
||||
|
||||
ASSERT_NE(entt::resolve(entt::type_id<int>()), entt::meta_type{});
|
||||
ASSERT_EQ(entt::resolve(ctx, entt::type_id<int>()), entt::meta_type{});
|
||||
ASSERT_TRUE(entt::resolve(entt::type_id<int>()).is_integral());
|
||||
|
||||
factory = entt::meta<int>(ctx);
|
||||
|
||||
ASSERT_NE(entt::resolve(entt::type_id<int>()), entt::meta_type{});
|
||||
ASSERT_NE(entt::resolve(ctx, entt::type_id<int>()), entt::meta_type{});
|
||||
ASSERT_TRUE(entt::resolve(ctx, entt::type_id<int>()).is_integral());
|
||||
}
|
||||
|
||||
TEST_F(MetaFactory, MetaReset) {
|
||||
using namespace entt::literals;
|
||||
|
||||
|
||||
+123
-24
@@ -58,6 +58,10 @@ struct function {
|
||||
return value * iv;
|
||||
}
|
||||
|
||||
[[nodiscard]] int f(int iv) {
|
||||
return (value += iv);
|
||||
}
|
||||
|
||||
void g(int iv) {
|
||||
value = iv * iv;
|
||||
}
|
||||
@@ -70,7 +74,7 @@ struct function {
|
||||
instance.value = iv;
|
||||
}
|
||||
|
||||
[[nodiscard]] int v(int &iv) const {
|
||||
[[nodiscard]] const int &v(int &iv) const {
|
||||
return (iv = value);
|
||||
}
|
||||
|
||||
@@ -107,8 +111,8 @@ struct MetaFunc: ::testing::Test {
|
||||
.type("derived"_hs)
|
||||
.base<base>()
|
||||
.func<&base::setter>("setter_from_base"_hs)
|
||||
.func<&base::getter>("getter_from_base"_hs)
|
||||
.func<&base::static_setter>("static_setter_from_base"_hs);
|
||||
.func<&base::getter>("getter_from_base")
|
||||
.func<&base::static_setter>("static_setter_from_base"_hs, "static setter");
|
||||
|
||||
entt::meta_factory<function>{}
|
||||
.type("func"_hs)
|
||||
@@ -121,6 +125,10 @@ struct MetaFunc: ::testing::Test {
|
||||
.custom<int>(2)
|
||||
.func<entt::overload<int(int) const>(&function::f)>("f1"_hs)
|
||||
.traits(test::meta_traits::one)
|
||||
.func<entt::overload<int(int) const>(&function::f)>("cf"_hs)
|
||||
.func<entt::overload<int(int)>(&function::f)>("cf"_hs)
|
||||
.func<entt::overload<int(int)>(&function::f)>("ncf"_hs)
|
||||
.func<entt::overload<int(int) const>(&function::f)>("ncf"_hs)
|
||||
.func<&function::g>("g"_hs)
|
||||
.custom<char>('c')
|
||||
.func<function::h>("h"_hs)
|
||||
@@ -129,6 +137,12 @@ struct MetaFunc: ::testing::Test {
|
||||
.func<&function::a, entt::as_ref_t>("a"_hs)
|
||||
.func<&function::a, entt::as_cref_t>("ca"_hs)
|
||||
.conv<int>();
|
||||
|
||||
entt::meta_factory<function>{}
|
||||
.func<&function::a, entt::as_is_t>("ar"_hs)
|
||||
.func<&function::v, entt::as_is_t>("vc"_hs)
|
||||
.func<&function::operator int, entt::as_is_t>("ov"_hs)
|
||||
.func<&function::g, entt::as_is_t>("ge"_hs);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
@@ -158,27 +172,6 @@ struct MetaFunc: ::testing::Test {
|
||||
|
||||
using MetaFuncDeathTest = MetaFunc;
|
||||
|
||||
TEST_F(MetaFunc, SafeWhenEmpty) {
|
||||
const entt::meta_func func{};
|
||||
entt::meta_any *args = nullptr;
|
||||
|
||||
ASSERT_FALSE(func);
|
||||
ASSERT_EQ(func, entt::meta_func{});
|
||||
ASSERT_EQ(func.arity(), 0u);
|
||||
ASSERT_FALSE(func.is_const());
|
||||
ASSERT_FALSE(func.is_static());
|
||||
ASSERT_EQ(func.ret(), entt::meta_type{});
|
||||
ASSERT_EQ(func.arg(0u), entt::meta_type{});
|
||||
ASSERT_EQ(func.arg(1u), entt::meta_type{});
|
||||
ASSERT_FALSE(func.invoke({}, args, 0u));
|
||||
ASSERT_FALSE(func.invoke({}, args, 1u));
|
||||
ASSERT_FALSE(func.invoke({}));
|
||||
ASSERT_FALSE(func.invoke({}, 'c'));
|
||||
ASSERT_EQ(func.traits<test::meta_traits>(), test::meta_traits::none);
|
||||
ASSERT_EQ(static_cast<const char *>(func.custom()), nullptr);
|
||||
ASSERT_EQ(func.next(), func);
|
||||
}
|
||||
|
||||
TEST_F(MetaFunc, UserTraits) {
|
||||
using namespace entt::literals;
|
||||
|
||||
@@ -216,6 +209,16 @@ ENTT_DEBUG_TEST_F(MetaFuncDeathTest, Custom) {
|
||||
ASSERT_DEATH([[maybe_unused]] const char value = entt::resolve<function>().func("h"_hs).custom(), "");
|
||||
}
|
||||
|
||||
TEST_F(MetaFunc, Name) {
|
||||
using namespace entt::literals;
|
||||
|
||||
const entt::meta_type type = entt::resolve<derived>();
|
||||
|
||||
ASSERT_EQ(type.func("setter_from_base"_hs).name(), nullptr);
|
||||
ASSERT_STREQ(type.func("getter_from_base"_hs).name(), "getter_from_base");
|
||||
ASSERT_STREQ(type.func("static_setter_from_base"_hs).name(), "static setter");
|
||||
}
|
||||
|
||||
TEST_F(MetaFunc, Comparison) {
|
||||
using namespace entt::literals;
|
||||
|
||||
@@ -506,6 +509,38 @@ TEST_F(MetaFunc, AsVoid) {
|
||||
ASSERT_EQ(value, instance.value);
|
||||
}
|
||||
|
||||
TEST_F(MetaFunc, AsIs) {
|
||||
using namespace entt::literals;
|
||||
|
||||
auto type = entt::resolve<function>();
|
||||
entt::meta_func func{};
|
||||
function instance{3};
|
||||
|
||||
func = type.func("ar"_hs);
|
||||
|
||||
ASSERT_TRUE(func);
|
||||
ASSERT_EQ(func.ret(), entt::resolve<int>());
|
||||
ASSERT_EQ(func.invoke(instance).base().policy(), entt::any_policy::ref);
|
||||
|
||||
func = type.func("vc"_hs);
|
||||
|
||||
ASSERT_TRUE(func);
|
||||
ASSERT_EQ(func.ret(), entt::resolve<int>());
|
||||
ASSERT_EQ(func.invoke(instance, 3).base().policy(), entt::any_policy::cref);
|
||||
|
||||
func = type.func("ov"_hs);
|
||||
|
||||
ASSERT_TRUE(func);
|
||||
ASSERT_EQ(func.ret(), entt::resolve<int>());
|
||||
ASSERT_EQ(func.invoke(instance).base().policy(), entt::any_policy::embedded);
|
||||
|
||||
func = type.func("ge"_hs);
|
||||
|
||||
ASSERT_TRUE(func);
|
||||
ASSERT_EQ(func.ret(), entt::resolve<void>());
|
||||
ASSERT_EQ(func.invoke(instance).base().policy(), entt::any_policy::empty);
|
||||
}
|
||||
|
||||
TEST_F(MetaFunc, AsRef) {
|
||||
using namespace entt::literals;
|
||||
|
||||
@@ -633,6 +668,70 @@ TEST_F(MetaFunc, Overloaded) {
|
||||
ASSERT_EQ(static_cast<const int &>(type.func("f2"_hs).next().custom()), 3);
|
||||
}
|
||||
|
||||
TEST_F(MetaFunc, OverloadedConstFirst) {
|
||||
using namespace entt::literals;
|
||||
|
||||
auto func = entt::resolve<function>().func("cf"_hs);
|
||||
auto next = func.next();
|
||||
function instance{2};
|
||||
|
||||
ASSERT_TRUE(func);
|
||||
ASSERT_EQ(func.arity(), 1u);
|
||||
ASSERT_TRUE(func.is_const());
|
||||
ASSERT_EQ(func.ret(), entt::resolve<int>());
|
||||
ASSERT_EQ(func.arg(0u), entt::resolve<int>());
|
||||
|
||||
ASSERT_TRUE(next);
|
||||
ASSERT_EQ(next.arity(), 1u);
|
||||
ASSERT_FALSE(next.is_const());
|
||||
ASSERT_EQ(next.ret(), entt::resolve<int>());
|
||||
ASSERT_EQ(next.arg(0u), entt::resolve<int>());
|
||||
|
||||
ASSERT_EQ(func.invoke(instance, 1).cast<int>(), 2);
|
||||
ASSERT_EQ(next.invoke(instance, 1).cast<int>(), 3);
|
||||
|
||||
ASSERT_EQ(func.invoke(std::as_const(instance), 1).cast<int>(), 3);
|
||||
ASSERT_FALSE(next.invoke(std::as_const(instance), 1));
|
||||
|
||||
ASSERT_EQ(func.invoke(entt::meta_handle{instance}, entt::meta_any{1}).cast<int>(), 3);
|
||||
ASSERT_EQ(next.invoke(entt::meta_handle{instance}, entt::meta_any{1}).cast<int>(), 4);
|
||||
|
||||
ASSERT_EQ(func.invoke(entt::meta_handle{std::as_const(instance)}, entt::meta_any{1}).cast<int>(), 4);
|
||||
ASSERT_FALSE(next.invoke(entt::meta_handle{std::as_const(instance)}, entt::meta_any{1}));
|
||||
}
|
||||
|
||||
TEST_F(MetaFunc, OverloadedNonConstFirst) {
|
||||
using namespace entt::literals;
|
||||
|
||||
auto func = entt::resolve<function>().func("ncf"_hs);
|
||||
auto next = func.next();
|
||||
function instance{2};
|
||||
|
||||
ASSERT_TRUE(func);
|
||||
ASSERT_EQ(func.arity(), 1u);
|
||||
ASSERT_FALSE(func.is_const());
|
||||
ASSERT_EQ(func.ret(), entt::resolve<int>());
|
||||
ASSERT_EQ(func.arg(0u), entt::resolve<int>());
|
||||
|
||||
ASSERT_TRUE(next);
|
||||
ASSERT_EQ(next.arity(), 1u);
|
||||
ASSERT_TRUE(next.is_const());
|
||||
ASSERT_EQ(next.ret(), entt::resolve<int>());
|
||||
ASSERT_EQ(next.arg(0u), entt::resolve<int>());
|
||||
|
||||
ASSERT_EQ(func.invoke(instance, 1).cast<int>(), 3);
|
||||
ASSERT_EQ(next.invoke(instance, 1).cast<int>(), 3);
|
||||
|
||||
ASSERT_FALSE(func.invoke(std::as_const(instance), 1));
|
||||
ASSERT_EQ(next.invoke(std::as_const(instance), 1).cast<int>(), 3);
|
||||
|
||||
ASSERT_EQ(func.invoke(entt::meta_handle{instance}, entt::meta_any{1}).cast<int>(), 4);
|
||||
ASSERT_EQ(next.invoke(entt::meta_handle{instance}, entt::meta_any{1}).cast<int>(), 4);
|
||||
|
||||
ASSERT_FALSE(func.invoke(entt::meta_handle{std::as_const(instance)}, entt::meta_any{1}));
|
||||
ASSERT_EQ(next.invoke(entt::meta_handle{std::as_const(instance)}, entt::meta_any{1}).cast<int>(), 4);
|
||||
}
|
||||
|
||||
TEST_F(MetaFunc, OverloadedOrder) {
|
||||
using namespace entt::literals;
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <utility>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/core/hashed_string.hpp>
|
||||
#include <entt/locator/locator.hpp>
|
||||
#include <entt/meta/context.hpp>
|
||||
#include <entt/meta/factory.hpp>
|
||||
#include <entt/meta/meta.hpp>
|
||||
|
||||
@@ -41,22 +43,12 @@ TEST_F(MetaHandle, Handle) {
|
||||
ASSERT_FALSE(handle);
|
||||
ASSERT_FALSE(chandle);
|
||||
|
||||
ASSERT_EQ(handle, chandle);
|
||||
ASSERT_EQ(handle, entt::meta_handle{});
|
||||
ASSERT_FALSE(handle != handle);
|
||||
ASSERT_TRUE(handle == handle);
|
||||
|
||||
handle = entt::meta_handle{instance};
|
||||
chandle = entt::meta_handle{std::as_const(instance)};
|
||||
|
||||
ASSERT_TRUE(handle);
|
||||
ASSERT_TRUE(chandle);
|
||||
|
||||
ASSERT_EQ(handle, chandle);
|
||||
ASSERT_NE(handle, entt::meta_handle{});
|
||||
ASSERT_FALSE(handle != handle);
|
||||
ASSERT_TRUE(handle == handle);
|
||||
|
||||
ASSERT_TRUE(handle->invoke("incr"_hs));
|
||||
ASSERT_FALSE(chandle->invoke("incr"_hs));
|
||||
ASSERT_FALSE(std::as_const(handle)->invoke("incr"_hs));
|
||||
@@ -71,3 +63,46 @@ TEST_F(MetaHandle, Handle) {
|
||||
ASSERT_FALSE(std::as_const(handle)->invoke("decr"_hs));
|
||||
ASSERT_EQ(instance.value, 0);
|
||||
}
|
||||
|
||||
TEST_F(MetaHandle, Value) {
|
||||
int value{2};
|
||||
entt::meta_handle handle{value};
|
||||
entt::meta_handle chandle{std::as_const(value)};
|
||||
|
||||
ASSERT_NE(handle->try_cast<int>(), nullptr);
|
||||
ASSERT_NE(handle->try_cast<const int>(), nullptr);
|
||||
ASSERT_EQ(chandle->try_cast<int>(), nullptr);
|
||||
ASSERT_NE(chandle->try_cast<const int>(), nullptr);
|
||||
|
||||
ASSERT_EQ(&handle->context(), &entt::locator<entt::meta_ctx>::value_or());
|
||||
ASSERT_EQ(&chandle->context(), &entt::locator<entt::meta_ctx>::value_or());
|
||||
}
|
||||
|
||||
TEST_F(MetaHandle, MetaAny) {
|
||||
entt::meta_any value{2};
|
||||
entt::meta_handle handle{value};
|
||||
entt::meta_handle chandle{std::as_const(value)};
|
||||
|
||||
ASSERT_NE(handle->try_cast<int>(), nullptr);
|
||||
ASSERT_NE(handle->try_cast<const int>(), nullptr);
|
||||
ASSERT_EQ(chandle->try_cast<int>(), nullptr);
|
||||
ASSERT_NE(chandle->try_cast<const int>(), nullptr);
|
||||
|
||||
ASSERT_EQ(&handle->context(), &entt::locator<entt::meta_ctx>::value_or());
|
||||
ASSERT_EQ(&chandle->context(), &entt::locator<entt::meta_ctx>::value_or());
|
||||
}
|
||||
|
||||
TEST_F(MetaHandle, ScopedMetaAny) {
|
||||
const entt::meta_ctx ctx{};
|
||||
entt::meta_any value{ctx, 2};
|
||||
entt::meta_handle handle{value};
|
||||
entt::meta_handle chandle{std::as_const(value)};
|
||||
|
||||
ASSERT_NE(handle->try_cast<int>(), nullptr);
|
||||
ASSERT_NE(handle->try_cast<const int>(), nullptr);
|
||||
ASSERT_EQ(chandle->try_cast<int>(), nullptr);
|
||||
ASSERT_NE(chandle->try_cast<const int>(), nullptr);
|
||||
|
||||
ASSERT_EQ(&handle->context(), &ctx);
|
||||
ASSERT_EQ(&chandle->context(), &ctx);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,10 @@ Type get(Type &elem) {
|
||||
|
||||
struct base {
|
||||
char value{'c'};
|
||||
|
||||
static int f() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct derived: base {
|
||||
@@ -127,17 +131,18 @@ struct MetaType: ::testing::Test {
|
||||
.data<set<double>, get<double>>("var"_hs);
|
||||
|
||||
entt::meta_factory<unsigned int>{}
|
||||
.type("unsigned int"_hs)
|
||||
.type("unsigned int"_hs, "uint")
|
||||
.traits(test::meta_traits::two)
|
||||
.data<0u>("min"_hs)
|
||||
.data<128u>("max"_hs);
|
||||
|
||||
entt::meta_factory<base>{}
|
||||
.type("base"_hs)
|
||||
.data<&base::value>("value"_hs);
|
||||
.data<&base::value>("value"_hs)
|
||||
.func<&base::f>("func"_hs);
|
||||
|
||||
entt::meta_factory<derived>{}
|
||||
.type("derived"_hs)
|
||||
.type("derived")
|
||||
.traits(test::meta_traits::one | test::meta_traits::three)
|
||||
.base<base>();
|
||||
|
||||
@@ -157,7 +162,11 @@ struct MetaType: ::testing::Test {
|
||||
.func<entt::overload<int(int, int)>(&overloaded_func::f)>("f"_hs)
|
||||
.func<entt::overload<int(int)>(&overloaded_func::f)>("f"_hs)
|
||||
.func<entt::overload<int(int) const>(&overloaded_func::f)>("f"_hs)
|
||||
.func<entt::overload<float(int, float)>(&overloaded_func::f)>("f"_hs);
|
||||
.func<entt::overload<float(int, float)>(&overloaded_func::f)>("f"_hs)
|
||||
.func<entt::overload<int(int) const>(&overloaded_func::f)>("cf"_hs)
|
||||
.func<entt::overload<int(int)>(&overloaded_func::f)>("cf"_hs)
|
||||
.func<entt::overload<int(int)>(&overloaded_func::f)>("ncf"_hs)
|
||||
.func<entt::overload<int(int) const>(&overloaded_func::f)>("ncf"_hs);
|
||||
|
||||
entt::meta_factory<property_type>{}
|
||||
.type("property"_hs)
|
||||
@@ -214,7 +223,7 @@ TEST_F(MetaType, SafeWhenEmpty) {
|
||||
ASSERT_FALSE(type);
|
||||
ASSERT_EQ(type, entt::meta_type{});
|
||||
ASSERT_EQ(type.info(), entt::type_id<void>());
|
||||
ASSERT_EQ(type.id(), entt::id_type{});
|
||||
ASSERT_EQ(type.id(), entt::type_id<void>().hash());
|
||||
ASSERT_EQ(type.size_of(), 0u);
|
||||
ASSERT_FALSE(type.is_arithmetic());
|
||||
ASSERT_FALSE(type.is_integral());
|
||||
@@ -233,9 +242,9 @@ TEST_F(MetaType, SafeWhenEmpty) {
|
||||
ASSERT_EQ(type.template_arg(0u), type);
|
||||
ASSERT_EQ(type.template_arg(1u), type);
|
||||
ASSERT_FALSE(type.can_cast(type));
|
||||
ASSERT_FALSE(type.can_cast(entt::resolve<void>()));
|
||||
ASSERT_FALSE(type.can_convert(type));
|
||||
ASSERT_FALSE(type.can_convert(entt::resolve<void>()));
|
||||
ASSERT_TRUE(type.can_cast(entt::resolve<void>()));
|
||||
ASSERT_TRUE(type.can_convert(type));
|
||||
ASSERT_TRUE(type.can_convert(entt::resolve<void>()));
|
||||
ASSERT_EQ(type.base().begin(), type.base().end());
|
||||
ASSERT_EQ(type.data().begin(), type.data().end());
|
||||
ASSERT_EQ(type.data("data"_hs), entt::meta_data{});
|
||||
@@ -301,6 +310,15 @@ TEST_F(MetaType, IdAndInfo) {
|
||||
ASSERT_EQ(type.info(), entt::type_id<clazz>());
|
||||
}
|
||||
|
||||
TEST_F(MetaType, Name) {
|
||||
using namespace entt::literals;
|
||||
|
||||
ASSERT_EQ(entt::resolve<base>().name(), nullptr);
|
||||
ASSERT_STREQ(entt::resolve<derived>().name(), "derived");
|
||||
ASSERT_STREQ(entt::resolve<unsigned int>().name(), "uint");
|
||||
ASSERT_EQ(entt::resolve<void>().name(), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(MetaType, SizeOf) {
|
||||
ASSERT_EQ(entt::resolve<void>().size_of(), 0u);
|
||||
ASSERT_EQ(entt::resolve<int>().size_of(), sizeof(int));
|
||||
@@ -442,6 +460,17 @@ TEST_F(MetaType, Data) {
|
||||
ASSERT_EQ(type.data().cbegin(), type.data().cend());
|
||||
}
|
||||
|
||||
TEST_F(MetaType, DataRecursive) {
|
||||
using namespace entt::literals;
|
||||
|
||||
auto type = entt::resolve<derived>();
|
||||
const derived instance{};
|
||||
|
||||
ASSERT_TRUE(type.data("value"_hs));
|
||||
ASSERT_EQ('c', type.data("value"_hs).get(instance).cast<char>());
|
||||
ASSERT_FALSE(type.data("value"_hs, false));
|
||||
}
|
||||
|
||||
TEST_F(MetaType, Func) {
|
||||
using namespace entt::literals;
|
||||
|
||||
@@ -465,6 +494,17 @@ TEST_F(MetaType, Func) {
|
||||
ASSERT_EQ(type.func().cbegin(), type.func().cend());
|
||||
}
|
||||
|
||||
TEST_F(MetaType, FuncRecursive) {
|
||||
using namespace entt::literals;
|
||||
|
||||
auto type = entt::resolve<derived>();
|
||||
const derived instance{};
|
||||
|
||||
ASSERT_TRUE(type.func("func"_hs));
|
||||
ASSERT_EQ(0, type.func("func"_hs).invoke(instance).cast<int>());
|
||||
ASSERT_FALSE(type.func("func"_hs, false));
|
||||
}
|
||||
|
||||
TEST_F(MetaType, Invoke) {
|
||||
using namespace entt::literals;
|
||||
|
||||
@@ -543,6 +583,66 @@ TEST_F(MetaType, OverloadedFunc) {
|
||||
ASSERT_FALSE(type.invoke("f"_hs, instance, 4, 8.));
|
||||
}
|
||||
|
||||
TEST_F(MetaType, OverloadedFuncConstFirst) {
|
||||
using namespace entt::literals;
|
||||
|
||||
const auto type = entt::resolve<overloaded_func>();
|
||||
overloaded_func instance{2};
|
||||
entt::meta_any res{};
|
||||
|
||||
ASSERT_TRUE(type.func("cf"_hs));
|
||||
|
||||
res = type.invoke("cf"_hs, instance, 1);
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(res.cast<int>(), 4);
|
||||
|
||||
res = type.invoke("cf"_hs, std::as_const(instance), 1);
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(res.cast<int>(), 2);
|
||||
|
||||
res = type.invoke("cf"_hs, entt::meta_handle{instance}, entt::meta_any{1});
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(res.cast<int>(), 4);
|
||||
|
||||
res = type.invoke("cf"_hs, entt::meta_handle{std::as_const(instance)}, entt::meta_any{1});
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(res.cast<int>(), 2);
|
||||
}
|
||||
|
||||
TEST_F(MetaType, OverloadedFuncNonConstFirst) {
|
||||
using namespace entt::literals;
|
||||
|
||||
const auto type = entt::resolve<overloaded_func>();
|
||||
overloaded_func instance{2};
|
||||
entt::meta_any res{};
|
||||
|
||||
ASSERT_TRUE(type.func("ncf"_hs));
|
||||
|
||||
res = type.invoke("ncf"_hs, instance, 1);
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(res.cast<int>(), 4);
|
||||
|
||||
res = type.invoke("ncf"_hs, std::as_const(instance), 1);
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(res.cast<int>(), 2);
|
||||
|
||||
res = type.invoke("ncf"_hs, entt::meta_handle{instance}, entt::meta_any{1});
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(res.cast<int>(), 4);
|
||||
|
||||
res = type.invoke("ncf"_hs, entt::meta_handle{std::as_const(instance)}, entt::meta_any{1});
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(res.cast<int>(), 2);
|
||||
}
|
||||
|
||||
TEST_F(MetaType, OverloadedFuncOrder) {
|
||||
using namespace entt::literals;
|
||||
|
||||
|
||||
@@ -59,24 +59,44 @@ using MetaUtilityDeathTest = MetaUtility;
|
||||
TEST_F(MetaUtility, MetaDispatch) {
|
||||
int value = 2;
|
||||
|
||||
auto as_void = entt::meta_dispatch<entt::as_void_t>(value);
|
||||
auto as_ref = entt::meta_dispatch<entt::as_ref_t>(value);
|
||||
auto as_cref = entt::meta_dispatch<entt::as_cref_t>(value);
|
||||
auto as_is = entt::meta_dispatch(value);
|
||||
auto as_ref = entt::meta_dispatch<entt::as_ref_t>(value);
|
||||
auto as_void = entt::meta_dispatch<entt::as_void_t>(value);
|
||||
auto as_is_copy = entt::meta_dispatch<entt::as_is_t>(static_cast<int &&>(value));
|
||||
auto as_is_cref = entt::meta_dispatch<entt::as_is_t>(std::as_const(value));
|
||||
auto as_is_ref = entt::meta_dispatch<entt::as_is_t>(value);
|
||||
auto as_value = entt::meta_dispatch(value);
|
||||
|
||||
ASSERT_EQ(as_void.type(), entt::resolve<void>());
|
||||
ASSERT_EQ(as_ref.type(), entt::resolve<int>());
|
||||
ASSERT_EQ(as_cref.type(), entt::resolve<int>());
|
||||
ASSERT_EQ(as_is.type(), entt::resolve<int>());
|
||||
ASSERT_EQ(as_ref.type(), entt::resolve<int>());
|
||||
ASSERT_EQ(as_void.type(), entt::resolve<void>());
|
||||
ASSERT_EQ(as_is_copy.type(), entt::resolve<int>());
|
||||
ASSERT_EQ(as_is_cref.type(), entt::resolve<int>());
|
||||
ASSERT_EQ(as_is_ref.type(), entt::resolve<int>());
|
||||
ASSERT_EQ(as_value.type(), entt::resolve<int>());
|
||||
|
||||
ASSERT_EQ(as_cref.base().policy(), entt::any_policy::cref);
|
||||
ASSERT_EQ(as_ref.base().policy(), entt::any_policy::ref);
|
||||
ASSERT_EQ(as_void.base().policy(), entt::any_policy::empty);
|
||||
ASSERT_EQ(as_is_copy.base().policy(), entt::any_policy::embedded);
|
||||
ASSERT_EQ(as_is_cref.base().policy(), entt::any_policy::cref);
|
||||
ASSERT_EQ(as_is_ref.base().policy(), entt::any_policy::ref);
|
||||
ASSERT_EQ(as_value.base().policy(), entt::any_policy::embedded);
|
||||
|
||||
ASSERT_NE(as_is.try_cast<int>(), nullptr);
|
||||
ASSERT_NE(as_ref.try_cast<int>(), nullptr);
|
||||
ASSERT_EQ(as_cref.try_cast<int>(), nullptr);
|
||||
ASSERT_NE(as_cref.try_cast<const int>(), nullptr);
|
||||
ASSERT_NE(as_ref.try_cast<int>(), nullptr);
|
||||
ASSERT_NE(as_is_copy.try_cast<int>(), nullptr);
|
||||
ASSERT_EQ(as_is_cref.try_cast<int>(), nullptr);
|
||||
ASSERT_NE(as_is_ref.try_cast<int>(), nullptr);
|
||||
ASSERT_NE(as_value.try_cast<int>(), nullptr);
|
||||
|
||||
ASSERT_EQ(as_is.cast<int>(), 2);
|
||||
ASSERT_EQ(as_ref.cast<int>(), 2);
|
||||
ASSERT_EQ(as_cref.cast<int>(), 2);
|
||||
ASSERT_EQ(as_ref.cast<int>(), 2);
|
||||
ASSERT_EQ(as_is_copy.cast<int>(), 2);
|
||||
ASSERT_EQ(as_is_cref.cast<int>(), 2);
|
||||
ASSERT_EQ(as_is_ref.cast<int>(), 2);
|
||||
ASSERT_EQ(as_value.cast<int>(), 2);
|
||||
}
|
||||
|
||||
TEST_F(MetaUtility, MetaDispatchMetaAny) {
|
||||
@@ -162,30 +182,30 @@ TEST_F(MetaUtility, MetaGetter) {
|
||||
clazz instance{};
|
||||
|
||||
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::static_getter>(invalid)));
|
||||
ASSERT_EQ((entt::meta_getter<clazz, &clazz::static_getter>(entt::meta_any{}.context(), instance)).cast<int>(), 0);
|
||||
ASSERT_EQ((entt::meta_getter<clazz, &clazz::static_getter>(instance)).cast<int>(), 0);
|
||||
|
||||
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::getter>(invalid)));
|
||||
ASSERT_EQ((entt::meta_getter<clazz, &clazz::getter>(entt::meta_any{}.context(), instance)).cast<int>(), 0);
|
||||
ASSERT_EQ((entt::meta_getter<clazz, &clazz::getter>(instance)).cast<int>(), 0);
|
||||
|
||||
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::member>(invalid)));
|
||||
ASSERT_EQ((entt::meta_getter<clazz, &clazz::member>(entt::meta_any{}.context(), instance)).cast<int>(), 0);
|
||||
ASSERT_EQ((entt::meta_getter<clazz, &clazz::member>(instance)).cast<int>(), 0);
|
||||
ASSERT_EQ((entt::meta_getter<clazz, &clazz::member>(std::as_const(instance))).cast<int>(), 0);
|
||||
|
||||
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::cmember>(invalid)));
|
||||
ASSERT_EQ((entt::meta_getter<clazz, &clazz::cmember>(entt::meta_any{}.context(), instance)).cast<int>(), 0);
|
||||
ASSERT_EQ((entt::meta_getter<clazz, &clazz::cmember>(instance)).cast<int>(), 0);
|
||||
ASSERT_EQ((entt::meta_getter<clazz, &clazz::cmember>(std::as_const(instance))).cast<int>(), 0);
|
||||
|
||||
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::arr>(invalid)));
|
||||
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::arr>(entt::meta_any{}.context(), instance)));
|
||||
ASSERT_FALSE((entt::meta_getter<clazz, &clazz::arr>(instance)));
|
||||
|
||||
ASSERT_EQ((entt::meta_getter<clazz, &clazz::value>(invalid)).cast<int>(), 0);
|
||||
ASSERT_EQ((entt::meta_getter<clazz, &clazz::value>(entt::meta_any{}.context(), instance)).cast<int>(), 0);
|
||||
ASSERT_EQ((entt::meta_getter<clazz, &clazz::value>(instance)).cast<int>(), 0);
|
||||
|
||||
ASSERT_EQ((entt::meta_getter<clazz, &clazz::cvalue>(invalid)).cast<int>(), 0);
|
||||
ASSERT_EQ((entt::meta_getter<clazz, &clazz::cvalue>(entt::meta_any{}.context(), instance)).cast<int>(), 0);
|
||||
ASSERT_EQ((entt::meta_getter<clazz, &clazz::cvalue>(instance)).cast<int>(), 0);
|
||||
|
||||
ASSERT_EQ((entt::meta_getter<clazz, 1>(invalid)).cast<int>(), 1);
|
||||
ASSERT_EQ((entt::meta_getter<clazz, 1>(entt::meta_any{}.context(), instance)).cast<int>(), 1);
|
||||
ASSERT_EQ((entt::meta_getter<clazz, 1>(instance)).cast<int>(), 1);
|
||||
}
|
||||
|
||||
TEST_F(MetaUtility, MetaInvokeWithCandidate) {
|
||||
@@ -194,22 +214,22 @@ TEST_F(MetaUtility, MetaInvokeWithCandidate) {
|
||||
clazz::value = 3;
|
||||
|
||||
ASSERT_FALSE((entt::meta_invoke<clazz>({}, &clazz::setter, std::next(args.data()))));
|
||||
ASSERT_FALSE((entt::meta_invoke<clazz>(entt::meta_any{}.context(), {}, &clazz::getter, nullptr)));
|
||||
ASSERT_FALSE((entt::meta_invoke<clazz>({}, &clazz::getter, nullptr)));
|
||||
|
||||
ASSERT_TRUE((entt::meta_invoke<clazz>(args[0u], &clazz::setter, std::next(args.data()))));
|
||||
ASSERT_FALSE((entt::meta_invoke<clazz>(entt::meta_any{}.context(), args[0u], &clazz::setter, args.data())));
|
||||
ASSERT_FALSE((entt::meta_invoke<clazz>(args[0u], &clazz::setter, args.data())));
|
||||
ASSERT_EQ((entt::meta_invoke<clazz>(args[0u], &clazz::getter, nullptr)).cast<int>(), 4);
|
||||
ASSERT_FALSE((entt::meta_invoke<clazz>(entt::meta_any{}.context(), args[1u], &clazz::getter, nullptr)));
|
||||
ASSERT_FALSE((entt::meta_invoke<clazz>(args[1u], &clazz::getter, nullptr)));
|
||||
|
||||
ASSERT_EQ((entt::meta_invoke<clazz>({}, &clazz::get_value, nullptr)).cast<int>(), 3);
|
||||
ASSERT_TRUE((entt::meta_invoke<clazz>(entt::meta_any{}.context(), {}, &clazz::reset_value, nullptr)));
|
||||
ASSERT_TRUE((entt::meta_invoke<clazz>({}, &clazz::reset_value, nullptr)));
|
||||
ASSERT_EQ(args[0u].cast<clazz &>().value, 0);
|
||||
|
||||
const auto setter = [](int &value) { value = 3; };
|
||||
const auto getter = [](int value) { return value * 2; };
|
||||
|
||||
ASSERT_TRUE(entt::meta_invoke<test::empty>({}, setter, std::next(args.data())));
|
||||
ASSERT_EQ(entt::meta_invoke<test::empty>(entt::meta_any{}.context(), {}, getter, std::next(args.data())).cast<int>(), 6);
|
||||
ASSERT_EQ(entt::meta_invoke<test::empty>({}, getter, std::next(args.data())).cast<int>(), 6);
|
||||
}
|
||||
|
||||
TEST_F(MetaUtility, MetaInvoke) {
|
||||
@@ -218,15 +238,15 @@ TEST_F(MetaUtility, MetaInvoke) {
|
||||
clazz::value = 3;
|
||||
|
||||
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::setter>({}, std::next(args.data()))));
|
||||
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::getter>(entt::meta_any{}.context(), {}, nullptr)));
|
||||
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::getter>({}, nullptr)));
|
||||
|
||||
ASSERT_TRUE((entt::meta_invoke<clazz, &clazz::setter>(args[0u], std::next(args.data()))));
|
||||
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::setter>(entt::meta_any{}.context(), args[0u], args.data())));
|
||||
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::setter>(args[0u], args.data())));
|
||||
ASSERT_EQ((entt::meta_invoke<clazz, &clazz::getter>(args[0u], nullptr)).cast<int>(), 4);
|
||||
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::getter>(entt::meta_any{}.context(), args[1u], nullptr)));
|
||||
ASSERT_FALSE((entt::meta_invoke<clazz, &clazz::getter>(args[1u], nullptr)));
|
||||
|
||||
ASSERT_EQ((entt::meta_invoke<clazz, &clazz::get_value>({}, nullptr)).cast<int>(), 3);
|
||||
ASSERT_TRUE((entt::meta_invoke<clazz, &clazz::reset_value>(entt::meta_any{}.context(), {}, nullptr)));
|
||||
ASSERT_TRUE((entt::meta_invoke<clazz, &clazz::reset_value>({}, nullptr)));
|
||||
ASSERT_EQ(args[0u].cast<clazz &>().value, 0);
|
||||
}
|
||||
|
||||
|
||||
+5
-13
@@ -236,9 +236,9 @@ TYPED_TEST(Poly, Functionalities) {
|
||||
ASSERT_TRUE(alias);
|
||||
ASSERT_TRUE(value);
|
||||
|
||||
ASSERT_EQ(empty.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(empty.info(), entt::type_id<void>());
|
||||
ASSERT_EQ(in_place.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(alias.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(alias.info(), entt::type_id<impl>());
|
||||
ASSERT_EQ(value.type(), entt::type_id<impl>());
|
||||
|
||||
ASSERT_EQ(alias.data(), &instance);
|
||||
@@ -251,7 +251,7 @@ TYPED_TEST(Poly, Functionalities) {
|
||||
ASSERT_TRUE(empty);
|
||||
ASSERT_NE(empty.data(), nullptr);
|
||||
ASSERT_NE(std::as_const(empty).data(), nullptr);
|
||||
ASSERT_EQ(empty.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(empty.info(), entt::type_id<impl>());
|
||||
ASSERT_EQ(empty->get(), 0);
|
||||
|
||||
empty.template emplace<impl>(3);
|
||||
@@ -265,7 +265,7 @@ TYPED_TEST(Poly, Functionalities) {
|
||||
ASSERT_NE(ref.data(), nullptr);
|
||||
ASSERT_EQ(ref.data(), in_place.data());
|
||||
ASSERT_EQ(std::as_const(ref).data(), std::as_const(in_place).data());
|
||||
ASSERT_EQ(ref.type(), entt::type_id<impl>());
|
||||
ASSERT_EQ(ref.info(), entt::type_id<impl>());
|
||||
ASSERT_EQ(ref->get(), 3);
|
||||
|
||||
poly_type null{};
|
||||
@@ -288,7 +288,7 @@ TYPED_TEST(Poly, Functionalities) {
|
||||
move.reset();
|
||||
|
||||
ASSERT_FALSE(move);
|
||||
ASSERT_EQ(move.type(), entt::type_id<void>());
|
||||
ASSERT_EQ(move.info(), entt::type_id<void>());
|
||||
}
|
||||
|
||||
TYPED_TEST(Poly, Owned) {
|
||||
@@ -436,17 +436,13 @@ TYPED_TEST(Poly, SboAlignment) {
|
||||
std::array<poly_type, 2u> sbo = {over_aligned{}, over_aligned{}};
|
||||
const auto *data = sbo[0].data();
|
||||
|
||||
// NOLINTBEGIN(*-reinterpret-cast)
|
||||
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[0u].data()) % alignment) == 0u);
|
||||
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[1u].data()) % alignment) == 0u);
|
||||
// NOLINTEND(*-reinterpret-cast)
|
||||
|
||||
std::swap(sbo[0], sbo[1]);
|
||||
|
||||
// NOLINTBEGIN(*-reinterpret-cast)
|
||||
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[0u].data()) % alignment) == 0u);
|
||||
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(sbo[1u].data()) % alignment) == 0u);
|
||||
// NOLINTEND(*-reinterpret-cast)
|
||||
|
||||
ASSERT_NE(data, sbo[1].data());
|
||||
}
|
||||
@@ -458,17 +454,13 @@ TYPED_TEST(Poly, NoSboAlignment) {
|
||||
std::array<poly_type, 2u> nosbo = {over_aligned{}, over_aligned{}};
|
||||
const auto *data = nosbo[0].data();
|
||||
|
||||
// NOLINTBEGIN(*-reinterpret-cast)
|
||||
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[0u].data()) % alignment) == 0u);
|
||||
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[1u].data()) % alignment) == 0u);
|
||||
// NOLINTEND(*-reinterpret-cast)
|
||||
|
||||
std::swap(nosbo[0], nosbo[1]);
|
||||
|
||||
// NOLINTBEGIN(*-reinterpret-cast)
|
||||
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[0u].data()) % alignment) == 0u);
|
||||
ASSERT_TRUE((reinterpret_cast<std::uintptr_t>(nosbo[1u].data()) % alignment) == 0u);
|
||||
// NOLINTEND(*-reinterpret-cast)
|
||||
|
||||
ASSERT_EQ(data, nosbo[1].data());
|
||||
}
|
||||
|
||||
+172
-111
@@ -1,46 +1,26 @@
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/process/process.hpp>
|
||||
#include "../../common/empty.h"
|
||||
|
||||
template<typename Delta>
|
||||
struct fake_process: entt::process<fake_process<Delta>, Delta> {
|
||||
using process_type = entt::process<fake_process<Delta>, Delta>;
|
||||
using delta_type = typename process_type::delta_type;
|
||||
class test_process: public entt::basic_process<Delta> {
|
||||
using entt::basic_process<Delta>::basic_process;
|
||||
|
||||
void succeed() noexcept {
|
||||
process_type::succeed();
|
||||
}
|
||||
|
||||
void fail() noexcept {
|
||||
process_type::fail();
|
||||
}
|
||||
|
||||
void pause() noexcept {
|
||||
process_type::pause();
|
||||
}
|
||||
|
||||
void unpause() noexcept {
|
||||
process_type::unpause();
|
||||
}
|
||||
|
||||
void init() {
|
||||
init_invoked = true;
|
||||
}
|
||||
|
||||
void succeeded() {
|
||||
void succeeded() override {
|
||||
succeeded_invoked = true;
|
||||
}
|
||||
|
||||
void failed() {
|
||||
void failed() override {
|
||||
failed_invoked = true;
|
||||
}
|
||||
|
||||
void aborted() {
|
||||
void aborted() override {
|
||||
aborted_invoked = true;
|
||||
}
|
||||
|
||||
void update(typename entt::process<fake_process<Delta>, Delta>::delta_type, void *data) {
|
||||
void update(const Delta, void *data) override {
|
||||
if(data != nullptr) {
|
||||
(*static_cast<int *>(data))++;
|
||||
}
|
||||
@@ -48,15 +28,29 @@ struct fake_process: entt::process<fake_process<Delta>, Delta> {
|
||||
update_invoked = true;
|
||||
}
|
||||
|
||||
bool init_invoked{};
|
||||
public:
|
||||
bool update_invoked{};
|
||||
bool succeeded_invoked{};
|
||||
bool failed_invoked{};
|
||||
bool aborted_invoked{};
|
||||
};
|
||||
|
||||
class test_no_update_process: public entt::process {
|
||||
void aborted() override {
|
||||
aborted_invoked = true;
|
||||
}
|
||||
|
||||
public:
|
||||
bool aborted_invoked{};
|
||||
};
|
||||
|
||||
class test_plain_process: public entt::process {
|
||||
using delta_type = typename entt::process::delta_type;
|
||||
void update(const delta_type, void *) override {}
|
||||
};
|
||||
|
||||
TEST(Process, Basics) {
|
||||
fake_process<int> process{};
|
||||
test_process<int> process{};
|
||||
|
||||
ASSERT_FALSE(process.alive());
|
||||
ASSERT_FALSE(process.finished());
|
||||
@@ -110,8 +104,32 @@ TEST(Process, Basics) {
|
||||
ASSERT_TRUE(process.rejected());
|
||||
}
|
||||
|
||||
TEST(Process, Succeeded) {
|
||||
fake_process<test::empty> process{};
|
||||
TEST(Process, SucceedNoOverride) {
|
||||
test_plain_process process{};
|
||||
|
||||
ASSERT_FALSE(process.alive());
|
||||
|
||||
process.tick(0u);
|
||||
|
||||
ASSERT_TRUE(process.alive());
|
||||
ASSERT_FALSE(process.finished());
|
||||
ASSERT_FALSE(process.rejected());
|
||||
|
||||
process.succeed();
|
||||
|
||||
ASSERT_FALSE(process.alive());
|
||||
ASSERT_FALSE(process.finished());
|
||||
ASSERT_FALSE(process.rejected());
|
||||
|
||||
process.tick(0u);
|
||||
|
||||
ASSERT_FALSE(process.alive());
|
||||
ASSERT_TRUE(process.finished());
|
||||
ASSERT_FALSE(process.rejected());
|
||||
}
|
||||
|
||||
TEST(Process, Succeed) {
|
||||
test_process<test::empty> process{};
|
||||
|
||||
process.tick({});
|
||||
process.tick({});
|
||||
@@ -123,15 +141,38 @@ TEST(Process, Succeeded) {
|
||||
ASSERT_FALSE(process.paused());
|
||||
ASSERT_FALSE(process.rejected());
|
||||
|
||||
ASSERT_TRUE(process.init_invoked);
|
||||
ASSERT_TRUE(process.update_invoked);
|
||||
ASSERT_TRUE(process.succeeded_invoked);
|
||||
ASSERT_FALSE(process.failed_invoked);
|
||||
ASSERT_FALSE(process.aborted_invoked);
|
||||
}
|
||||
|
||||
TEST(Process, FailNoOverride) {
|
||||
test_plain_process process{};
|
||||
|
||||
ASSERT_FALSE(process.alive());
|
||||
|
||||
process.tick(0u);
|
||||
|
||||
ASSERT_TRUE(process.alive());
|
||||
ASSERT_FALSE(process.finished());
|
||||
ASSERT_FALSE(process.rejected());
|
||||
|
||||
process.fail();
|
||||
|
||||
ASSERT_FALSE(process.alive());
|
||||
ASSERT_FALSE(process.finished());
|
||||
ASSERT_FALSE(process.rejected());
|
||||
|
||||
process.tick(0u);
|
||||
|
||||
ASSERT_FALSE(process.alive());
|
||||
ASSERT_FALSE(process.finished());
|
||||
ASSERT_TRUE(process.rejected());
|
||||
}
|
||||
|
||||
TEST(Process, Fail) {
|
||||
fake_process<int> process{};
|
||||
test_process<int> process{};
|
||||
|
||||
process.tick(0);
|
||||
process.tick(0);
|
||||
@@ -143,15 +184,88 @@ TEST(Process, Fail) {
|
||||
ASSERT_FALSE(process.paused());
|
||||
ASSERT_TRUE(process.rejected());
|
||||
|
||||
ASSERT_TRUE(process.init_invoked);
|
||||
ASSERT_TRUE(process.update_invoked);
|
||||
ASSERT_FALSE(process.succeeded_invoked);
|
||||
ASSERT_TRUE(process.failed_invoked);
|
||||
ASSERT_FALSE(process.aborted_invoked);
|
||||
}
|
||||
|
||||
TEST(Process, AbortNoOverride) {
|
||||
test_plain_process process{};
|
||||
|
||||
ASSERT_FALSE(process.alive());
|
||||
|
||||
process.tick(0u);
|
||||
|
||||
ASSERT_TRUE(process.alive());
|
||||
ASSERT_FALSE(process.finished());
|
||||
ASSERT_FALSE(process.rejected());
|
||||
|
||||
process.abort();
|
||||
|
||||
ASSERT_FALSE(process.alive());
|
||||
ASSERT_FALSE(process.finished());
|
||||
ASSERT_FALSE(process.rejected());
|
||||
|
||||
process.tick(0u);
|
||||
|
||||
ASSERT_FALSE(process.alive());
|
||||
ASSERT_FALSE(process.finished());
|
||||
ASSERT_TRUE(process.rejected());
|
||||
}
|
||||
|
||||
TEST(Process, NoUpdateAbort) {
|
||||
test_no_update_process process{};
|
||||
|
||||
ASSERT_FALSE(process.alive());
|
||||
ASSERT_FALSE(process.rejected());
|
||||
ASSERT_FALSE(process.aborted_invoked);
|
||||
|
||||
process.tick(0u);
|
||||
|
||||
ASSERT_FALSE(process.alive());
|
||||
ASSERT_TRUE(process.rejected());
|
||||
ASSERT_TRUE(process.aborted_invoked);
|
||||
}
|
||||
|
||||
TEST(Process, AbortNextTick) {
|
||||
test_process<int> process{};
|
||||
|
||||
process.tick(0);
|
||||
process.abort();
|
||||
process.tick(0);
|
||||
|
||||
ASSERT_FALSE(process.alive());
|
||||
ASSERT_FALSE(process.finished());
|
||||
ASSERT_FALSE(process.paused());
|
||||
ASSERT_TRUE(process.rejected());
|
||||
|
||||
ASSERT_TRUE(process.update_invoked);
|
||||
ASSERT_FALSE(process.succeeded_invoked);
|
||||
ASSERT_FALSE(process.failed_invoked);
|
||||
ASSERT_TRUE(process.aborted_invoked);
|
||||
}
|
||||
|
||||
TEST(Process, AbortImmediately) {
|
||||
test_process<test::empty> process{};
|
||||
|
||||
process.tick({});
|
||||
process.abort();
|
||||
process.tick({});
|
||||
|
||||
ASSERT_FALSE(process.alive());
|
||||
ASSERT_FALSE(process.finished());
|
||||
ASSERT_FALSE(process.paused());
|
||||
ASSERT_TRUE(process.rejected());
|
||||
|
||||
ASSERT_TRUE(process.update_invoked);
|
||||
ASSERT_FALSE(process.succeeded_invoked);
|
||||
ASSERT_FALSE(process.failed_invoked);
|
||||
ASSERT_TRUE(process.aborted_invoked);
|
||||
}
|
||||
|
||||
TEST(Process, Data) {
|
||||
fake_process<test::empty> process{};
|
||||
test_process<test::empty> process{};
|
||||
int value = 0;
|
||||
|
||||
process.tick({});
|
||||
@@ -165,97 +279,44 @@ TEST(Process, Data) {
|
||||
ASSERT_FALSE(process.rejected());
|
||||
|
||||
ASSERT_EQ(value, 1);
|
||||
ASSERT_TRUE(process.init_invoked);
|
||||
ASSERT_TRUE(process.update_invoked);
|
||||
ASSERT_TRUE(process.succeeded_invoked);
|
||||
ASSERT_FALSE(process.failed_invoked);
|
||||
ASSERT_FALSE(process.aborted_invoked);
|
||||
}
|
||||
|
||||
TEST(Process, AbortNextTick) {
|
||||
fake_process<int> process{};
|
||||
TEST(Process, ThenPeek) {
|
||||
test_process<int> process{};
|
||||
|
||||
process.tick(0);
|
||||
process.abort();
|
||||
process.tick(0);
|
||||
ASSERT_FALSE(process.peek());
|
||||
|
||||
ASSERT_FALSE(process.alive());
|
||||
ASSERT_FALSE(process.finished());
|
||||
ASSERT_FALSE(process.paused());
|
||||
ASSERT_TRUE(process.rejected());
|
||||
process.then<test_process<int>>().then<test_process<int>>();
|
||||
|
||||
ASSERT_TRUE(process.init_invoked);
|
||||
ASSERT_FALSE(process.update_invoked);
|
||||
ASSERT_FALSE(process.succeeded_invoked);
|
||||
ASSERT_FALSE(process.failed_invoked);
|
||||
ASSERT_TRUE(process.aborted_invoked);
|
||||
ASSERT_TRUE(process.peek());
|
||||
ASSERT_TRUE(process.peek()->peek());
|
||||
ASSERT_FALSE(process.peek()->peek()->peek());
|
||||
// peek does not release ownership
|
||||
ASSERT_TRUE(process.peek());
|
||||
}
|
||||
|
||||
TEST(Process, AbortImmediately) {
|
||||
fake_process<test::empty> process{};
|
||||
TEST(Process, Handle) {
|
||||
auto process = std::make_shared<test_process<int>>();
|
||||
auto handle = process->shared_from_this();
|
||||
|
||||
process.tick({});
|
||||
process.abort(true);
|
||||
|
||||
ASSERT_FALSE(process.alive());
|
||||
ASSERT_FALSE(process.finished());
|
||||
ASSERT_FALSE(process.paused());
|
||||
ASSERT_TRUE(process.rejected());
|
||||
|
||||
ASSERT_TRUE(process.init_invoked);
|
||||
ASSERT_FALSE(process.update_invoked);
|
||||
ASSERT_FALSE(process.succeeded_invoked);
|
||||
ASSERT_FALSE(process.failed_invoked);
|
||||
ASSERT_TRUE(process.aborted_invoked);
|
||||
ASSERT_TRUE(handle);
|
||||
ASSERT_EQ(process.get(), handle.get());
|
||||
}
|
||||
|
||||
TEST(ProcessAdaptor, Resolved) {
|
||||
bool updated = false;
|
||||
auto lambda = [&updated](std::uint64_t, void *, auto resolve, auto) {
|
||||
ASSERT_FALSE(updated);
|
||||
updated = true;
|
||||
resolve();
|
||||
};
|
||||
TEST(Process, CustomAllocator) {
|
||||
const std::allocator<void> allocator{};
|
||||
entt::process process{allocator};
|
||||
|
||||
auto process = entt::process_adaptor<decltype(lambda), std::uint64_t>{lambda};
|
||||
ASSERT_EQ(process.get_allocator(), allocator);
|
||||
ASSERT_FALSE(process.get_allocator() != allocator);
|
||||
|
||||
process.tick(0);
|
||||
process.tick(0);
|
||||
const entt::process &other = process.then([](entt::process &, std::uint32_t, void *) {});
|
||||
|
||||
ASSERT_TRUE(process.finished());
|
||||
ASSERT_TRUE(updated);
|
||||
}
|
||||
|
||||
TEST(ProcessAdaptor, Rejected) {
|
||||
bool updated = false;
|
||||
auto lambda = [&updated](std::uint64_t, void *, auto, auto rejected) {
|
||||
ASSERT_FALSE(updated);
|
||||
updated = true;
|
||||
rejected();
|
||||
};
|
||||
|
||||
auto process = entt::process_adaptor<decltype(lambda), std::uint64_t>{lambda};
|
||||
|
||||
process.tick(0);
|
||||
process.tick(0);
|
||||
|
||||
ASSERT_TRUE(process.rejected());
|
||||
ASSERT_TRUE(updated);
|
||||
}
|
||||
|
||||
TEST(ProcessAdaptor, Data) {
|
||||
int value = 0;
|
||||
|
||||
auto lambda = [](std::uint64_t, void *data, auto resolve, auto) {
|
||||
*static_cast<int *>(data) = 2;
|
||||
resolve();
|
||||
};
|
||||
|
||||
auto process = entt::process_adaptor<decltype(lambda), std::uint64_t>{lambda};
|
||||
|
||||
process.tick(0);
|
||||
process.tick(0, &value);
|
||||
|
||||
ASSERT_TRUE(process.finished());
|
||||
ASSERT_EQ(value, 2);
|
||||
ASSERT_NE(&process, &other);
|
||||
ASSERT_EQ(other.get_allocator(), allocator);
|
||||
ASSERT_FALSE(other.get_allocator() != allocator);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
@@ -5,34 +6,46 @@
|
||||
#include <entt/process/process.hpp>
|
||||
#include <entt/process/scheduler.hpp>
|
||||
|
||||
struct foo_process: entt::process<foo_process, entt::scheduler::delta_type> {
|
||||
foo_process(std::function<void()> upd, std::function<void()> abort)
|
||||
: on_update{std::move(upd)}, on_aborted{std::move(abort)} {}
|
||||
|
||||
void update(delta_type, void *) const {
|
||||
class foo_process: public entt::process {
|
||||
void update(const delta_type, void *) override {
|
||||
on_update();
|
||||
}
|
||||
|
||||
void aborted() const {
|
||||
void aborted() override {
|
||||
on_aborted();
|
||||
}
|
||||
|
||||
public:
|
||||
using allocator_type = typename entt::process::allocator_type;
|
||||
|
||||
foo_process(const allocator_type &allocator, std::function<void()> upd, std::function<void()> abort)
|
||||
: entt::process{allocator},
|
||||
on_update{std::move(upd)},
|
||||
on_aborted{std::move(abort)} {}
|
||||
|
||||
private:
|
||||
std::function<void()> on_update;
|
||||
std::function<void()> on_aborted;
|
||||
};
|
||||
|
||||
struct succeeded_process: entt::process<succeeded_process, entt::scheduler::delta_type> {
|
||||
void update(delta_type, void *data) {
|
||||
class succeeded_process: public entt::process {
|
||||
void update(const delta_type, void *data) override {
|
||||
++static_cast<std::pair<int, int> *>(data)->first;
|
||||
succeed();
|
||||
}
|
||||
|
||||
public:
|
||||
using entt::process::process;
|
||||
};
|
||||
|
||||
struct failed_process: entt::process<failed_process, entt::scheduler::delta_type> {
|
||||
void update(delta_type, void *data) {
|
||||
class failed_process: public entt::process {
|
||||
void update(const delta_type, void *data) override {
|
||||
++static_cast<std::pair<int, int> *>(data)->second;
|
||||
fail();
|
||||
}
|
||||
|
||||
public:
|
||||
using entt::process::process;
|
||||
};
|
||||
|
||||
TEST(Scheduler, Functionalities) {
|
||||
@@ -47,9 +60,7 @@ TEST(Scheduler, Functionalities) {
|
||||
ASSERT_EQ(scheduler.size(), 0u);
|
||||
ASSERT_TRUE(scheduler.empty());
|
||||
|
||||
scheduler.attach<foo_process>(
|
||||
[&updated]() { updated = true; },
|
||||
[&aborted]() { aborted = true; });
|
||||
scheduler.attach<foo_process>([&updated]() { updated = true; }, [&aborted]() { aborted = true; });
|
||||
|
||||
ASSERT_NE(scheduler.size(), 0u);
|
||||
ASSERT_FALSE(scheduler.empty());
|
||||
@@ -74,7 +85,7 @@ TEST(Scheduler, Swap) {
|
||||
entt::scheduler other{};
|
||||
int counter{};
|
||||
|
||||
scheduler.attach([&counter](auto &&...) { ++counter; });
|
||||
scheduler.attach([&counter](entt::process &, std::uint32_t, void *) { ++counter; });
|
||||
|
||||
ASSERT_EQ(scheduler.size(), 1u);
|
||||
ASSERT_EQ(other.size(), 0u);
|
||||
@@ -96,22 +107,37 @@ TEST(Scheduler, Swap) {
|
||||
ASSERT_EQ(counter, 2);
|
||||
}
|
||||
|
||||
TEST(Scheduler, Then) {
|
||||
TEST(Scheduler, SharedFromThis) {
|
||||
entt::scheduler scheduler{};
|
||||
auto &process = scheduler.attach<succeeded_process>();
|
||||
const auto &then = process.then<failed_process>();
|
||||
auto other = process.shared_from_this();
|
||||
|
||||
ASSERT_TRUE(other);
|
||||
ASSERT_NE(&process, &then);
|
||||
ASSERT_EQ(&process, other.get());
|
||||
ASSERT_EQ(process.get_allocator(), scheduler.get_allocator());
|
||||
ASSERT_EQ(process.get_allocator(), other->get_allocator());
|
||||
ASSERT_EQ(then.get_allocator(), scheduler.get_allocator());
|
||||
}
|
||||
|
||||
TEST(Scheduler, AttachThen) {
|
||||
entt::scheduler scheduler{};
|
||||
std::pair<int, int> counter{};
|
||||
|
||||
scheduler
|
||||
// failing process with successor
|
||||
.attach<succeeded_process>()
|
||||
// failing process with successor
|
||||
scheduler.attach<succeeded_process>()
|
||||
.then<succeeded_process>()
|
||||
.then<failed_process>()
|
||||
.then<succeeded_process>();
|
||||
|
||||
// failing process without successor
|
||||
scheduler.attach<succeeded_process>()
|
||||
.then<succeeded_process>()
|
||||
// failing process without successor
|
||||
.attach<succeeded_process>()
|
||||
.then<succeeded_process>()
|
||||
.then<failed_process>()
|
||||
// non-failing process
|
||||
.attach<succeeded_process>()
|
||||
.then<failed_process>();
|
||||
|
||||
// non-failing process
|
||||
scheduler.attach<succeeded_process>()
|
||||
.then<succeeded_process>();
|
||||
|
||||
while(!scheduler.empty()) {
|
||||
@@ -128,19 +154,18 @@ TEST(Scheduler, Functor) {
|
||||
bool first_functor = false;
|
||||
bool second_functor = false;
|
||||
|
||||
auto attach = [&first_functor](auto, void *, auto resolve, auto) {
|
||||
ASSERT_FALSE(first_functor);
|
||||
first_functor = true;
|
||||
resolve();
|
||||
};
|
||||
|
||||
auto then = [&second_functor](auto, void *, auto, auto reject) {
|
||||
ASSERT_FALSE(second_functor);
|
||||
second_functor = true;
|
||||
reject();
|
||||
};
|
||||
|
||||
scheduler.attach(std::move(attach)).then(std::move(then)).then([](auto...) { FAIL(); });
|
||||
scheduler
|
||||
.attach([&first_functor](entt::process &proc, std::uint32_t, void *) {
|
||||
ASSERT_FALSE(first_functor);
|
||||
first_functor = true;
|
||||
proc.succeed();
|
||||
})
|
||||
.then([&second_functor](entt::process &proc, std::uint32_t, void *) {
|
||||
ASSERT_FALSE(second_functor);
|
||||
second_functor = true;
|
||||
proc.fail();
|
||||
})
|
||||
.then([](entt::process &, std::uint32_t, void *) { FAIL(); });
|
||||
|
||||
while(!scheduler.empty()) {
|
||||
scheduler.update(0);
|
||||
@@ -155,9 +180,9 @@ TEST(Scheduler, SpawningProcess) {
|
||||
entt::scheduler scheduler{};
|
||||
std::pair<int, int> counter{};
|
||||
|
||||
scheduler.attach([&scheduler](auto, void *, auto resolve, auto) {
|
||||
scheduler.attach([&scheduler](entt::process &proc, std::uint32_t, void *) {
|
||||
scheduler.attach<succeeded_process>().then<failed_process>();
|
||||
resolve();
|
||||
proc.succeed();
|
||||
});
|
||||
|
||||
while(!scheduler.empty()) {
|
||||
@@ -175,7 +200,7 @@ TEST(Scheduler, CustomAllocator) {
|
||||
ASSERT_EQ(scheduler.get_allocator(), allocator);
|
||||
ASSERT_FALSE(scheduler.get_allocator() != allocator);
|
||||
|
||||
scheduler.attach([](auto &&...) {});
|
||||
scheduler.attach([](entt::process &, std::uint32_t, void *) {});
|
||||
const decltype(scheduler) other{std::move(scheduler), allocator};
|
||||
|
||||
ASSERT_EQ(other.size(), 1u);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user