Compare commits

...

163 Commits

Author SHA1 Message Date
d5dede5a61 properly cleanup in main 2024-03-12 18:43:36 +01:00
719400068a cache the localtime result in the message
localtime uses global state AND spams allocations like cazy
2024-03-12 18:09:42 +01:00
aaf8c6adc1 save cooldown for tox profile (10sec after last) and save on exit 2024-03-12 18:09:21 +01:00
dc081ae2aa fix webp mem leak 2024-03-11 20:46:28 +01:00
3cf3097094 toxcore update + private interface update 2024-03-11 11:46:18 +01:00
e801626232 Merge commit 'a5093c4aa36d66049b6b5ca94e3b17412e77391b' 2024-03-11 11:34:13 +01:00
a5093c4aa3 Squashed 'external/toxcore/c-toxcore/' changes from b03b571272..3e05824b80
3e05824b80 refactor: Rename `out` parameters to `out_$something`.
0199c0f17f cleanup: apply the same scheme to types
aebbfabe26 cleanup: event length naming inconsistencies
2457125aa8 cleanup: align group send err enum order

git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: 3e05824b80eb9bee33e8254cba0780d84c522182
2024-03-11 11:34:13 +01:00
c311bb5c95 update tox interface 2024-03-10 20:50:13 +01:00
887705969f make texture streaming actually work 2024-03-09 16:26:35 +01:00
316871523d update nixos target 2024-03-09 11:50:55 +01:00
a3d193516c add updating to textures 2024-03-08 22:04:58 +01:00
6709dba0e7 remove old test 2024-03-08 12:29:48 +01:00
a8d8613f2c adopt to new toxcore master 2024-03-08 10:50:58 +01:00
aae086cc65 Squashed 'external/toxcore/c-toxcore/' changes from e2c01e457b..b03b571272
b03b571272 fix: flaky tcp test This only fixes the symptoms, not the real problem. Sometimes or consistently on some platforms a socket might need a moment before it can be written to.
32e67ab4c2 cleanup: use typedef for private message ID's in callback
7b1db6adc1 feat: add message IDs to private group messages
99e0bcc27d refactor: Observers/ignored peers can now send and receive custom packets
b3c3c49d26 fix: Disable IPv6 in Windows cross-compilation tests
e742deddff feat: Check hashes of Windows dependencies when cross-compiling
dfb9a0b02b fix: Test the current Windows Dockerfile, not an old Dockerhub image
14de93ccec chore: Use WineHQ's Wine as Debian Bookworm's crashes
ed37616249 docs: Update the Windows cross-compilation section
9bb79c174f cleanup: Remove a couple of unnecessary misc_tools dependencies
19475adb70 chore: Statically link OpenMP into the cracker fun util on Windows
1be311e51f feat: Build the fun utils when cross-compiling to Windows
88133f8446 chore: Strip Windows binaries
3cc0ae7535 refactor: Copy over all of the required static dependencies
c4fa8f7fb1 feat: Generate .def, .exp and .lib files when building for Windows
74bbac5363 feat: Let CMake create the dll instead of doing so ourselves
246642e9ae feat: Harden Windows cross-compilation
8d431c0d11 chore: Bump Windows build dependency versions
e519f7998b fix: Remove unnecessary wsock32 dependency on Windows
ed2b60c217 chore: Use a specific non-broken slimcc version.
d7f21010a1 chore: Update github actions.
e71a68b7f2 docs: Update the list of CMake options
77e08876ff chore: Remove mod and founder from group API naming scheme
12bc042767 docs: add the experimental api build option to INSTALL.md
e1fa5cae96 refactor: Rename Queries to Query to align with other enums.
be82a3ea30 fix: Correct type for conference offline peer numbers.
0627c36716 test: Add pkgsrc build.
92578afe4b test: Add FreeBSD VM action on GitHub.
52ece0f57b test: Build toxcore on NetBSD (VM).
3fe8ee2c11 chore: Only install tox_private.h on request.
9a8dfa06ab fix: save_compatibility_test failing on big-endian systems
86f5e55578 fix: Don't serve files from websockify.
710eb674a5 fix: Correctly pass extended public keys to group moderation code.
021db7031c refactor: Use `struct`s for extended public/secret keys.
a1e999fd80 chore: Compile libsodium reference implementation with compcert.
fbe3c19cf5 cleanup: correct a few nullable annotations
623e3ee5c3 cleanup: Don't use `memcpy` to cast arbitrary `struct`s to `uint8_t[]`.
c71567dc18 fix: Pass array, not array pointer, to `memcmp`.
9b46a08144 cleanup: Never pass `void*` directly to `memcpy`.
5d7b7a7bbc refactor: Use tox rng to seed the keypair generation.
961891d568 cleanup: Small improvements found by PVS Studio.
8201019f0d chore: Disable NGC saving by default, enable through Tox_Options.
5dd9ee3f65 cleanup: Replace pointer arithmetic with explicit `&arr[i]`.
ca4606d49d refactor: Use strong typedef for NGC peer id.
442213b722 cleanup: Simplify custom packet length check in NGC.
08d3393def fix: Correct a few potential null derefs in bootstrap daemon.
b9877b32b0 fix: Add missing memunlock of local variable when it goes out of scope.
dab5fe44b9 fix: Zero out stack-allocated secret key before return.
f058103299 refactor: Make prune_gc_sanctions_list more obviously correct.
3ba7a0dec9 docs: Add static analysis tool list to README.
8d0811a0f3 docs: Run prettier-markdown on markdown files.
969e3a2bfc refactor: Fix network test not using the strong typedef
93c83fbc7c refactor: Use strong typedef instead of struct for `Socket`.
9fe18b176f fix: Fix some false positive from PVS Studio.
7c44379ccb cleanup: Check that WINXP macro exists before comparing it.
5c93231bef refactor: Make tox mutex non-recursive.
aacff73939 docs: Fix up doxyfile.
d55fc85ff5 docs: Add more documentation to crypto_core.
5bdaaaedb6 refactor: Remove `Tox *` from `tox_dispatch`.
e202341e76 refactor: Don't rely on tox_dispatch passing tox in tests.
34df938f52 chore: Use C++ mode for clang-tidy.
8b05296a78 chore: Check that both gtest and gmock exist for tests.
42010660e1 test: Add slimcc compiler compatibility test.
b473630321 chore: Add some comments to the astyle config.
b7404f24f6 cleanup: Remove implicit bool conversions.
4e2dba4d9f chore: Reformat sources with astyle.
4359e3a6bc chore: Rename C++ headers to .hh suffixes.
0c05566e58 cleanup: Further `#include` cleanups.
8d29935b7a chore: Only check the bootstrap daemon checksum on release.
f70e588bc6 cleanup: Add more `const` where possible.
511bfe39c8 cleanup: Use Bazel modules to enforce proper `#include` hygiene.
1710a0d091 refactor: Move pack/unpack `IP_Port` from DHT into network module.
a975943564 chore: Really fix coverage docker image build.
c08409390f chore: Fix post-submit coverage image.
39aadf8922 fix: Don't use `memcmp` to compare `IP_Port`s.
d94246a906 fix: partially fix a bug that prevented group part messages from sending.
eeaa039222 chore: Fix rpm build; add a CI check for it.
8328449c1a chore: Speed up docker builds a bit by reducing layer count.
d6d67d56f3 cleanup: Add `const` where possible in auto tests.
6aa9e6850d cleanup: Minor cleanup of event unpack code.
bdf460a3a9 refactor: Rename `system_{memory,...}` to `os_{memory,...}`.
203e1af81e fix: a few off by one errors in group autotests
5c093c4888 cleanup: Remove all uses of `SIZEOF_VLA`.
662c2140f3 test: Add goblint static analyser.
8f07755834 cleanup: Use `memzero(x, s)` instead of `memset(x, 0, s)`.
a7258e40cf cleanup: Use explicit 0 instead of `PACKET_ID_PADDING`.
6370d0f15d cleanup: Expand the `Tox_Options` accessor macros.
14a1a0b9bd cleanup: Remove plan9 support.
a05dccad13 test: Add a simple new/delete test for Tox.
1cdcf938b9 cleanup: Add comment after every `#endif`.
ba99d4dc4b test: Fix comment I broke in the events test PR.
e07248debb refactor: Migrate auto_tests to new events API.
bdd42b5452 refactor: Add common msgpack array packer with callback.
3c659f5288 cleanup: Rename group to conference in groupav documentation.
89957be230 cleanup: Ensure handler params are named after callback params.
c650d9d345 refactor: Pass `this` pointer as first param to s11n callbacks.
e7fb91ddb8 refactor: Allow NULL pointers for byte arrays in events.
5e2c8cabc1 cleanup: make some improvements to group moderation test
259de4867e cleanup: Remove `bin_pack_{new,free}`.
21a8ff5895 cleanup: skip a do_gc iteration before removing peers marked for deletion
16809dc36e feat: Add dht_get_nodes_response event to the events system.

git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: b03b5712720de9a9901ea12fd741f177327a7021
2024-03-07 23:12:55 +01:00
6ec6fe96e4 Merge commit 'aae086cc650e42eec1eea8db28cd01fa868d7f90' 2024-03-07 23:12:55 +01:00
2d31b57097 update msg 2024-03-07 23:08:26 +01:00
b1d4848b81 Merge commit '3678301916cce063f95c3f525d82e21c2a382cd6' as 'external/qoi/qoi' 2024-03-06 01:04:41 +01:00
3678301916 Squashed 'external/qoi/qoi/' content from commit 30d15d79b7
git-subtree-dir: external/qoi/qoi
git-subtree-split: 30d15d79b7
2024-03-06 01:04:41 +01:00
459ccf7d6b print sdl render 2024-03-06 01:02:59 +01:00
db54afd180 full on qoi 2024-03-06 01:02:59 +01:00
f4fe94dfe6 qoi lib 2024-03-06 01:02:59 +01:00
28b92b0f4c prep qoi 2024-03-04 12:38:39 +01:00
c966fc6954 update sub changes coming from fs/os research 2024-03-04 11:28:51 +01:00
d0761bf60e revert crop by default (did not work as intended) 2024-02-29 19:15:33 +01:00
0f41ee6a2e add screencap to readme 2024-02-23 11:20:49 +01:00
0aeafec019 fix month starting at 0 2024-02-15 15:34:44 +01:00
9a0df4f577 date change 2024-02-15 15:29:01 +01:00
61714836bb typos 2024-02-12 14:13:57 +01:00
cff0c100ec add fragment store draft doc 2024-02-11 19:01:48 +01:00
010c49d100 stable names 2024-02-10 12:23:40 +01:00
ff5dbaffc0 fix some file selector glitches 2024-02-08 18:11:51 +01:00
b56d581e4b fix normal feeling sluggish 2024-02-05 16:15:10 +01:00
aa661aaaa7 default to normal fps mode again 2024-02-05 16:10:30 +01:00
cc3f430bab rework tc and move tcs out of cg into main screen, rework render pp
now respecting animation timing
2024-02-05 16:06:12 +01:00
139db5b03b faster texture cache loading in low fps modes 2024-02-05 12:50:36 +01:00
7d0e5c80bd lil dep update 2024-02-04 12:48:04 +01:00
f716ad9dd1 limit max main loop sleep 2024-02-03 20:49:52 +01:00
671772a20e min fps for inactive reduced now 1fps 2024-02-03 19:07:14 +01:00
b0173f6d68 tox iterate interval pow(1.6)
fix faux offline inbetween timer
crop by default
2024-02-03 15:00:32 +01:00
3da5872df8 fix tffom and have it actually functioning 2024-02-03 01:05:50 +01:00
3deb6e8469 fix using bool for timestamps (oops) 2024-02-02 20:55:20 +01:00
0c674e0137 add tox friend faux offline message (still wonky) + small file copy error handling 2024-02-02 20:26:50 +01:00
7948d820c3 fix filepaths on windows 2024-01-30 15:16:01 +01:00
5aac3422aa allow "copy file" which sets the text/uri-list with the file path 2024-01-30 11:58:01 +01:00
e8eaa7a232 Merge commit '04b33820291e8574ea2618aead9cd0be6a4df56b' 2024-01-27 13:03:42 +01:00
04b3382029 Squashed 'external/stb/stb/' changes from c39c7023eb..f4a71b1337
f4a71b1337 README.md: tweak credits
a8a25e17b5 update readme version numbers
0bc88af4de stb_image: optimizations
0ca75da4ec stb_image_resize2: remove whitespace
9d924f8a47 stb_image_resize: 2.04
4da08a1dbd stb_image: create_png_image_raw restructuring
c6d7c32e5d stb_image: Two warning fixes
07268cbf36 stb_image: New Paeth filter
ed64333410 tests: test_png_regress
45eb4ac158 global: very basic .gitignore for object files
e5f0e18d0f stb_image: Small PNG filter simplification
d373674115 stb_image: Fix zlib decoder end-of-stream handling
03f50e343d security notice
1d878bd2a3 security notice
beebb24b94 README.md: fix reference to stb_image_resize2.h
e81f294b16 stb_image_resize: switch GNU/clang from "asm" to "__asm__" for -c99 compatibility
c4bbb6e75f stb_image_resize2.h 2.00
REVERT: c39c7023eb stb_image: create_png_image_raw restructuring
REVERT: 8c3aa05487 stb_image: Two warning fixes
REVERT: 3aa1744a29 stb_image: New Paeth filter
REVERT: 2f3b7e47fa tests: test_png_regress
REVERT: d647af9ad2 global: very basic .gitignore for object files
REVERT: 5a44133dc5 stb_image: Small PNG filter simplification
REVERT: 9f1776a36d stb_image: Fix zlib decoder end-of-stream handling

git-subtree-dir: external/stb/stb
git-subtree-split: f4a71b13373436a2866c5d68f8f80ac6f0bc1ffe
2024-01-27 13:03:42 +01:00
7fe6df5889 update deps (fix uppercase hex and others) 2024-01-27 12:27:55 +01:00
2647c85323 add imgui stylign window + change how texture filters are applied 2024-01-21 20:20:32 +01:00
93140231c6 theme according to system 2024-01-21 14:28:56 +01:00
e76e56e025 larger font hack + linear texture filter for images 2024-01-21 13:58:22 +01:00
b1062e701e make primary selection pasting work
it is also bugged, as SDL misses large parts of selection sources (terminals etc) but browers work
2024-01-20 18:06:58 +01:00
e72aebc043 add auto release 2024-01-19 16:53:13 +01:00
865dfa994f make invisible images not render 2024-01-18 23:44:38 +01:00
25be42e308 correct imgui version and add vid 2024-01-18 18:36:29 +01:00
c6a0df409d interface versions 2024-01-18 00:32:11 +01:00
a15a9af2b3 adopt code to new imgui and new sdl 2024-01-17 22:39:51 +01:00
ca6909b64a imgui v1.90.1 Merge commit '5af8cfa8799b54f7549f6d730d498ecb42964032' 2024-01-17 21:11:13 +01:00
5af8cfa879 Squashed 'external/imgui/imgui/' changes from d4ddc46e77..d6cb3c923d
d6cb3c923d Version 1.90.1
6470e2279e Debug Tools: DebugRenderKeyboardPreview() scales better.
fdf8d02be1 Debug Tools: Added io.ConfigDebugIsDebuggerPresent and Debug Break buttons. (#2673)
788bb58b6b Metrics: Tweak, reorganize tools menu.
a5dec42866 Debug Tools: Debug Log: Clicking any filter with SHIFT held enables it for 2 frames only. (#5855)
a3eea8a75a Backends: OpenGL3: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" and variants, fixing regression on distros missing a symlink. (#6983)
69bf3291df Internals, Inputs: Fix for ImGuiInputFlags_RepeatUntilXXX logic when #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO is not set.
af00b182e3 Examples: SDL3: Minor fixes following changes to API (SDL_WindowFlags -> Uint32).
fc2e532f99 Shortcut: do not return true on mods changes. Internals: added ImGuiInputFlags_RepeatUntilKeyModsChange, ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone, ImGuiInputFlags_RepeatUntilOtherKeyPress. (#456, #2637)
8323a06e6d Inputs: passing ImGuiInputFlags_RepeatXXX options automatically adds ImGuiInputFlags_Repeat.
58261dbe9a Internals: alter ImGuiInputFlags values to leave room + indent.
f1ae47c4b9 Docs: update links to Proggy Fonts. (#7211)
7f9533b840 ColorPicker: Fixed saturation/value cursor radius not scaling properly.
27e83c2953 Add comment about how ImGuiKey values are named after US keyboard keys. (#7205)
278cf1a7bc Readme: updated binaries. (#7193)
edeb8ee3ab More compact issue_template.yml
c6716f5e9f Fixed typo (#7197)
a1b06823fe Windows: BeginChild(): Resize borders rendered even when ImGuiWindowFlags_NoBackground is specified. (#1710, #7194)
0461ade24b Reworked issue template (amends) (#5927, #5915)
c528b688cf Reworked issue template. (#5927, #5915)
26eef4df87 Update issue_template.md
7938550d52 Comments and some extra line-wrapping in demo (#3193)
4758f74676 imgui_freetype: fix nullptr to ImTextureID cast (#7192)
6f10cef2a1 Backends: Vulkan: moved structure declarations.
33d18c580b Misc: During shutdown, check that io.BackendPlatformUserData and io.BackendRendererUserData are NULL. (#7175)
0ea99132c8 Backends: Vulkan: Stop creating command pools with VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT as we don't reset them.
4778560e66 Backends: Vulkan: Added MinAllocationSize field in ImGui_ImplVulkan_InitInfo to workaround zealous validation layer. (#7189, #4238)
e8dd47effa Backends: WebGPU: Fixing an issue when opening a popup in the wgpu backend (#7191)
718fa0eec6 Happy new year!
240ab5890b Backends: GLFW, Input: Use Unicode version of WndProc for get correct input for text in utf-8 code page. (#7174)
4a2426449a Drags, Sliders, Inputs: removed all attempts to filter non-numerical characters during text editing. (#6810, #7096)
f039e69b9c Settings: Fixed an issue marking settings as dirty when merely clicking on a border or resize grip without moving it.
8340a30d27 Debug: move debug assertion in post-clip code to reduce overhead. (#4796 and more).
1e1013085b Debug Tools: Debug Log: Hide its own clipper log to reduce noise in the output.
036a6c875e ColorEdit4: Further tweaks for very small sizes. (#7120, #7121)
0bd6489721 DragScalarN, SliderScalarN, InputScalarN, PushMultiItemsWidths: fixed multi-components width in tight space (#7120, #7121)
0000739c08 Internals: Fixed function name typo.
33d426842d Backends: Vulkan: ImGui_ImplVulkan_CreateFontsTexture() calls vkQueueWaitIdle() instead of vkDeviceWaitIdle(). (#7148, #6943, #6715, #6327, #3743, #4618)
3cb805489b Backends: GLFW, Emscripten: fixes for canvas resizing, amends. (#6751)
22a7d241ff Backends: GLFW, Emscripten: fixes for canvas resizing. (#6751)
b4c5a83cfe Commented out obsolete ImGuiKey_KeyPadEnter redirection to ImGuiKey_KeypadEnter. (#2625, #7143)
70f2aaff43 Nav: tabbing happen within FocusScope. ImGuiWindowFlags_NavFlattened make window inherit focus scope from parent.
55073aa7a3 Examples; SDL: added missing return values checks from SDL_CreateWindow() calls. (#7147)
8764a1b7c4 Backends: Vulkan: free FontCommandBuffer explicitely (not actually required in normal code path, unless ImGui_ImplVulkan_DestroyDeviceObjects is declared directly). (#7104)
089ed30323 Replace usages of ImGuiKey_KeyPadEnter with ImGuiKey_KeypadEnter. (#7143)
e265610a0c Fixes for MSVC code analyzer.
f59b54c6f4 Nav: Activation can also be performed with Keypad Enter. (#5606)
0d582dabf3 Fixed warning (amend 54c1bde)
6cfe3ddf52 InputTextMultiline: Tabbing through a multi-line text editor using ImGuiInputTextFlags_AllowTabInput doesn't activate it. (#3092, #5759, #787)
54c1bdeceb Internals: removed unused ImGuiItemStatusFlags_FocusedByTabbing. (#4449)
4afffa36e9 InputTextMultiline: Fixed Tab character input not repeating
f6836ff37f Misc: Rework debug display of texture id in Metrics window (amend) (#7090)
07dbd46ddd Misc: Rework debug display of texture id in Metrics window to avoid compile-error when ImTextureID is defined to be larger than 64-bits. (#7090)
34646627aa ColorEdit4: improve components width computation to better distribute the error (#7120) (#7123)
86512eac06 DragScalarN, SliderScalarN, InputScalarN, PushMultiItemsWidths: improve multi-components width computation to better distribute the error. (#7120, #7121)
03298fe875 Windows: Fixed some auto-resizing path using style.WindowMinSize.x (instead of x/y).  (#7106)
69f524ba95 DragScalarN, SliderScalarN, InputScalarN, PushMultiItemsWidths: Added when component <= 0. (#7095)
9d8de45313 Image(): comment and minor refactor to resurface the fact that a border size may be added. (#2118)
5366bd09bf Scrolling: internal scrolling value is rounded instead of truncated. (#6677)
c58d2c89c3 Tabs: Added ImGuiTabItemFlags_NoAssumedClosure to enable app to react on closure attempt. (#7084)
1fade35159 DragScalarN, SliderScalarN, InputScalarN, PushMultiItemsWidths: Fixed incorrect pushes into ItemWidth stack when number of components is 1. [#7095]
58ca5f6424 Shortcut(): clearer early out in SetShortcutRouting() -> CalcRoutingScore() path.
d72e1563d4 Removed CalcListClipping() marked obsolete in 1.86. (#3841) + comments
9a2985611c Backend: Android: Remove Redundant Check (#7093)
0b77980cab Moved Tables API related declarations to their own section in imgui.h
aaf157cfdd Commented out ImGuiFreeType::BuildFontAtlas() obsoleted in 1.81. Commented out legacy ImGuiColumnsFlags_XXX symbols redirecting to ImGuiOldColumnsFlags_XXX, obsoleted in 1.80.
1fd5ff7152 Avoid C++26 removed deprecated arithmetic conversion on enumerations. (#7088, #7089. #2983, #3040)
52886872f1 Misc: Added IMGUI_USER_H_FILENAME to change the path included when using IMGUI_INCLUDE_IMGUI_USER_H. (#7039)
5768de79e2 InputText, ColorEdit, ColorPicker: better support for undocumented ImGuiItemFlags_ReadOnly flag. (#7079, #211)
b112d73edb Menus: amend to clarify/fix static analyzer warning.  (#6671, #6926)
2ee40d3cf9 Menus: Tweaked hover slack logic, adding a timer to avoid situations where a slow vertical movements toward another parent BeginMenu() can keep the wrong child menu open. (#6671, #6926)
b4b864e40a Backends: Vulkan: Fixed mismatching allocator passed to vkCreateCommandPool() vs vkDestroyCommandPool(). (#7075)
d2b0167610 Fixed link error when using IMGUI_DISABLE_DEBUG_TOOLS
7965494ff3 Debug Tools: Added DebugFlashStyleColor() to identify a style color. Added to Style Editor.
c1a3c7f445 Fixed Clang zealous warnings
c6ec69c7c1 Using nullptr in remaining examples/backends locations. (#6313, #7071, #4537)
888834c636 Backends: Android: Fixed build breaking typo. (#7060)
077e4db772 Misc: Added extra courtesy ==/!= operators when IMGUI_DEFINE_MATH_OPERATORS is defined.
6f171a066d Nav, IO: SetNextFrameWantCaptureKeyboard(false) calls are not overrided back to true when navigation is enabled. (#6997)
1f3090a48d Backends: SDL3: Fix indent (#7062)
86891b0986 Backends: Android: ImGui_ImplAndroid_HandleInputEvent() takes a const AInputEvent* (#7060)
f37f6f67f6  Windows: BeginChild(): Fixed auto-resizing erroneously limiting size to host viewport minus padding. (#7063)
bce4db00bc Drag and Drop: Fixed drop target highlight on items temporarily pushing a widened clip rect. (#7049, #4281, #3272)
65a10410e3 Docs: Add suggestion to turn of char8_t behavior for C++20 (#7025)
61b8197942 Misc: Renamed some defines in imstb_textedit.h to avoid conflicts when using unity/jumbo builds.
3733b5064e Backends: SDL3: Fixed unused variable warning on master on some targets. (#7000)
c5c1c4134b Backends: OpenGL3: imgui_impl_opengl3_loader.h: change #define ARRAY_SIZE to GL3W_ARRAY_SIZE to avoid external conflicts (#7017, #7018)
7bb0a525c3 Fixed text functions fast-path for handling "%s" and "%.*s" to handle null pointers gracefully. (#7016, #3466, #6846)
3d083dbe1a Version 1.90.1 WIP
b81bd7ed98 Version 1.90.0
623bff23ce Windows: WindowMinSize not applied on AlwaysAutoResize window. (amend e2035a5)
f298491a8a Docs: amend/tweak details about using io.WantCaptureMouse. (#7012)
5de1312e1c SplitterBehavior: tweak to not assert due to floating point precision.
afadf74a53 BeginChild: undo child name simplification as it reveal an issue with handling of ### in child names.
46843b683b Fonts: minor/shallow amends (#6925)
ade4d0e08a Fonts: added support for RasterizerDensity to FreeType based atlas generator. (#6925)
ed29ff08ba Fonts: added support for RasterizerDensity in built-in atlas generator. (#6925)
abfb9269b5 Fonts: added RasterizerDensity to ImFontConfig. (#6925)
add915bdc4 Backends: SDL3: Updates for recent API changes. (#7000)
4ad5817aac Update backends/language lists
75c46a3930 Demo: Partly fix resizing constraint demo. (#6210, #5627)
fe6544622b SetNextWindowSizeConstraints() clarified parameters, fixed comments. (#1139, #3186, #3270)
454f36d2af Removed 'bool border' legacy versions of BeginChild() as they seemingly have no value other than confusing user and IDE.
44dbad64d7 Backends: Vulkan: Fixes for VK_NO_PROTOTYPES.
79a9e2fdfb Backends: Vulkan: (Breaking) full font upload is performed by ImGui_ImplVulkan_CreateFontsTexture(), no need for user code to create or provide a command-buffer. Removed ImGui_ImplVulkan_DestroyFontUploadObjects(). (#6943, #6715, #6327, #3743, #4618)
6e7b43b6c7 Backends: Vulkan: Added ImGui_ImplVulkan_DestroyFontsTexture(), made ImGui_ImplVulkan_CreateFontsTexture() destroy previous one. (#6943, #6715, #6327, #3743, #4618)
d0da79c572 Defining IMGUI_DISABLE_OBSOLETE_FUNCTIONS now automaticaly define IMGUI_DISABLE_OBSOLETE_KEYIO. (#4921)
ab522dd18c Removed IM_OFFSETOF() macro in favor of using offsetof() available in C++11. Kept redirection define. (#4537)
0f50b52da4 Backends: OpenGL3: Update GL3W based imgui_impl_opengl3_loader.h to load "libGL.so" instead of "libGL.so.1". (#6983)
5170a9d6dc Tables: Internals: renamed TableDrawContextMenu() to TableDrawDefaultContextMenu() for clarify.
fea52e29aa Tables: added flags to TableDrawContextMenu() in order to display selected sections + added internal table->DisableDefaultContextMenu = true way to submit your own contents.
0d3b468cb3 BeginChild(): added ImGuiChildFlags_AutoResizeX, ImGuiChildFlags_AutoResizeY, ImGuiChildFlags_AlwaysAutoResize + support for SetNextWindowSizeConstraints(). (#1666, #1395, #1496, #1710) + Demo
44345c2108 Better documented the difference between BeginListBox() and BeginChild() w/ ImGuiChildFlags_FrameStyle.
fa4c49b4a7 BeginChild(): resizing is only clamped on axis where there's no scrollbar. Added an extra ResizeX in Demo->Simple Layout.
cdbc21a191 BeginChild(): Added ImGuiChildFlags_FrameStyle as a replacement for BeginChildFrame(). (#1666, #1496, #1395, #1710, #462, #503, #263)
ab47efd9a0 Bits / comments
561af15d67 Internal: Added owner aware variant of IsMouseDoubleClicked() for consistency.
376035fd01 Nav: fixed programmatic nav calls (e.g. SetKeyboardFocusHere() from storing io.KeyMods)
cfc71ab7c5 Made ImDrawCallback_ResetRenderState special value -8 instead of -1 so it is pointer aligned (#6969)
1ab63d925f Undid some of the changes done by c95fbb4 because they are not compatible with docking code.
c0bc43ccff Offset values for ImGuiWindowFlags_NoNavInputs, ImGuiWindowFlags_NoNavFocus, ImGuiWindowFlags_UnsavedDocument.
f1d1a8d32b Windows: use relative mouse movement for border resize when the border geometry has moved. (#1710)
9235352400 BeginChild: Added ImGuiChildFlags_ResizeX and ImGuiChildFlags_ResizeY. (#1710)
34a0bc456e BeginChild: Added ImGuiChildFlags_AlwaysUseWindowPadding, obsoleted ImGuiWindowFlags_AlwaysUseWindowPadding. (#462, (toward #1666, #1496, #1395, #1710)
7713c29258 BeginChild: Upgraded 'bool border = true' parameter to use a ImGuiChildFlags type and the ImGuiChildFlags_Border value. (toward #1666, #1496, #1395, #1710)
88fec09715 ColorPicker4(): Fixed ImGuiColorEditFlags_NoTooltip when ImGuiColorEditFlags_NoSidePreview is also set. (#6957)
313676d200 Settings: omit outputing Collapsed=0 in .ini file. Changelog + docs tweaks
8ee85137d8 BeginChild(): Internal name used by child windows now omits the hash/id if the child window is submitted in root of id stack of parent window.
4e4042bc33 Windows: tidying up skipitems logic at end of Begin(), normally should be no meaningful side-effect.
bc3c6e74e6 Windows: fixed double-clicked border from showing highlighted at the new position.
e2035a514c Windows: shared code for CalcWindowMinSize().
c95fbb4464 Windows: Double-clicking bottom or right window border auto-resize on a singles axis.
ade2acfd1d Inputs: Added IsKeyChordPressed() public helper function.
1b9cb52d7b BeginChild(): rename parameters to reduce diff of upcoming patches.
f8dc03d702 Windows: Can also auto-resize by double-clicking lower-left resize grip (not only lower-right one).
56f7e853be Demo: expose more Combo flags + misc tidying up.
9a5da23553 Changelog formatting
d6d00b4fcf Moved BeginChild() above BeginChildEx() as it is more readable.
99913b5051 Internals: added IsKeyChordPressed() for consistency.
a8bdbfddf9 Tables: Fixed top-most and left-most outer border overlapping inner clip-rect when scrolling. (#6765)
8db02ef8df Tables: Fixed an issue with ScrollX enabled where an extraneous draw command would be created.
947255c3da Tooltips: made it possible to use ImGuiHoveredFlags_ForTooltip + a ImGuiHoveredFlags_DelayXXXX override. (#1485)
0b8c6b9bce Internals: removed seemingly unused AutoFitChildAxises.
12a3c77c2f Demo: Minor tweak to angled headers demo.
f96c5443b1 Tables: fixed angled headers with frozen columns.
32228d8add Tables: added Angled headers support. Added ImGuiTableColumnFlags_AngledHeader, ImGui::TableHeadersAngledRow(), style.TableAngledHeadersAngle. (#2957)
9f851ebfe4 Tables: added ImGuiTableFlags_HighlightHoveredColumn.
be1311cfc1 Tables: fixed double-clicking on a column from clearing HoveredColumnBorder for a frame.
08606714a3 Fixed incorrect assert in FocusTopMostWindowUnderOne() preventing child+popup from being used. (#6915, #718)
5053d79a24 Tables: Internal: rework so stacked headers height may be used.
b9ebb8e06f Tables: fixed right-clicking right-most section (past right-most column) from highlighting right-most column.
feddcf3030 Combo: amends for ImGuiComboFlags_WidthFitPreview. (#6881)
112d8fc41d Combo: added ImGuiComboFlags_WidthFitPreview. (#6881)
001f102f38 IO, Backends: added ImGuiKey_AppBack, ImGuiKey_AppForward. (#6891, #4921)
7bbd758681 Backends: Win32: revert oops chunk.
b0758c86d8 Backends: Added support for extra ImGuiKey values: F13 to F24 function keys. (#6891, #4921)
73346e4341 IO: Add extra keys to ImGuiKey enumerator: ImGuiKey_F13 to ImGuiKey_F24. (#6891, #4921)
0312a29e4c ImageButton(): clarify purpose of size. (#6901, #5533, #4471, #2464, #1390).
1107bffe84 Popups: clarified meaning of 'p_open != NULL' in BeginPopupModal() + set back user value to false when popup is closed in ways other than clicking the close button. (#6900)
2c07d581de TreeNode: Added ImGuiTreeNodeFlags_SpanAllColumns for use in tables. (#3151, #3565, #2451, #2438)
085ed7bfbe Drag and Drop: Rework drop target highlight. (#4281, #3272)
0dd756bceb Moved GetCursorScreenPos/SetCursorScreenPos on top of its section.
a63e2f0a33 Drag and Drop: Fixed submitting a tooltip from drop target location. Added demo.
64b1aeebf5 Backends: OpenGL3: rename gl3w's loader symbols to allow LTO compilation with another copy of gl3w. (#6875, #6668, #4445)
28b237f94d Separator(): Altered end-points to use more standard boundaries. (#205, #4787, #1643, #759)
330d763477 Separator: clarified setting the ImGuiSeparatorFlags_SpanAllColumns flag. (#759)
a61438740d Debug Tools: Metrics: Added "Show groups rectangles" in tools.
701a047ac0 Fonts: Fix clang compiling warning & error with freetype + lunasvg (#6873, #6842, #6591)
03e2a7f584 Debug Tools: Rename ShowIdStackToolWindow() -> ShowIDStackToolWindow(). (#4631)
c21278eeae Debug Tools: Rename ShowIdStackToolWindow() -> ShowIDStackToolWindow(). (#4631)
2f431a948c IO: removed io.MetricsActiveAllocations introduced in 1.63. Same as 'g.DebugMemAllocCount - g.DebugMemFreeCount' (still displayed in Metrics.
f1519efb16 BeginGroup(): fix/amend 9ece0bd.
456aa3bc0a Menus: Fixed a bug where activating an item in a child-menu and dragging mouse over the parent-menu would erroneously close the child-menu. (#6869)
9ece0bdc02 BeginGroup(): Fixed a bug pushing line lower extent too far down when called after a call to SameLine() followed by manual cursor manipulation.
8175a47881 Debug Tools: Renamed ShowStackToolWindow() ("Stack Tool") to ShowIdStackToolWindow() ("ID Stack Tool"). (#4631)
204ae8a407 Internals: added ImRect::ContainsWithPad()
94da5842ef Renamed ImFloor() to ImTrunc(). Renamed ImFloorSigned() to ImFloor(). (#6861)
e5ca5351d5 TabBar: Fixed position of unsaved document marker (ImGuiTabItemFlags_UnsavedDocument) which was accidentally offset in 1.89.9. (#6862)
72ae6f5200 Fixed MousePosPrev with has never been valid outside of NewFrame().
12ee2dd789 Backends: Win32: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit it. (#6859)
d4869207e3 Misc: Most text functions also treat "%.*s" (along with "%s") specially to avoid formatting. (#3466, #6846)
f4790f6f66 BeginItemTooltip: Rename flag in comment (#6853)
daf49e9d82 Made ImFileOpen reuse a memory buffer so .ini saving doesn't allocate once every time. Added commented out MemAlloc/MemFree debug log.
d6360c1ba9 Fonts, imgui_freetype: Fixed a warning and leak in IMGUI_ENABLE_FREETYPE_LUNASVG support. (#6842, #6591)
6c022f9bf1 IO, Inputs: rename SetMousePos() to TeleportMousePos(). (#6837, #228)
ff36fe365e IO, Inputs: setting io.WantSetMousePos ignores incoming MousePos events. (#6837, #228)
ef8ff1b5d8 TabBar, Style: added style.TabBarBorderSize and associated ImGuiStyleVar_TabBarBorderSize. (#6820, #4859, #5022, #5239)
6addf28c4b Fonts: Ensure calling AddFontXXX function doesn't invalidates ImFont's ConfigData pointers prior to building again. (#6825)
b101cf46b6 ListBox, Combo: Changed signature of "name getter" callback in old one-liner ListBox()/Combo() apis.
8a2cd81091 TypingSelect: always enable nav highlight.
223b19f116 Fixed warning when builidng with IMGUI_DISABLE_DEBUG_TOOLS.
779568bb38 TypingSelect: tidy up some more + split ino functions to make reuse simpler.
661a70fc79 TypingSelect: fast switch between characters in + debug, internal renames.
32171a8b0c Fonts: Better assert during load when passing truncated font data or wrong data size. (#6822)
c32db3c72b Tables: Fixed an edge-case when no columns are visible + table scrollbar is visible + user code is always testing return value of TableSetColumnIndex() to coarse clip.
c86ce70968 Internal: Nav,MultiSelect: import ImGuiSelectionUserData, SetNextItemSelectionUserData() from MultiSelect. Track NavLastValidSelectionUserData as a convenience.
f336e639e9 TypingSelect: rework GetTypingSelectRequest(), provide TypingSelectFindResult().
9714594c35 Tooltips: made using SetItemTooltip()/IsItemHovered(ImGuiHoveredFlags_ForTooltip) defaults to activate tooltips on disabled items.. (#1485)
6eb2681c09 Internals: InitOrLoadWindowSettings() clear Size again for better data nuking in tests. Debug Log: added ImGuiDebugLogFlags_OutputToTestEngine flag.
bed492da70 Tables: amend support for auto-resize. (#6807)
556a1397a9 Tables: Remove comment referencing removed field SortSign (#6807)
3aceb61059 Tables: Request user to submit contents when outer host-window is requesting auto-resize. (#6510)
da21b74313 Tables: Fixed subtle drawing overlap between borders in some situations. (#2957, #6765)
5a483c2ffb Tables: Fixed top-most outer border being drawn with both TableBorderLight and TableBorderStrong in some situations, causing the earlier to be visible underneath when alpha is not 1.0f.
a34071876f Tables: Fixed bottom-most and right-most outer border offset by one. (#6765, #3752)
357f752bed Docs: add more links to the top of every examples and backends files.
b9ab6e2019 Nav: Tabbing always enable nav highlight when ImGuiConfigFlags_NavEnableKeyboard is set. (#6802, #3092, #5759, #787)
0e1ce76ea8 InputTextMultiline: Fixed Tabbing cycle leading to a situation where Enter key wouldn't be accepted by the widget when navigation highlight is visible. (#6802, #3092, #5759, #787)
bd63a9f056 Fonts: 'float size_pixels' passed to AddFontXXX() functions is now rounded to lowest integer. (#3164, #3309, #6800)
0962c9fb72 TypingSelect: Added first version of GetTypingSelectRequest() API.
7812039402 ImVector: Added find_index() helper.
44a6b493ee Commented out obsolete ImDrawCornerFlags_XXX. Commented out runtime support for hardcoded ~0 or 0x01..0x0F rounding flags values for AddRect()/AddRectFilled()/PathRect()/AddImageRounded()
becd75676f Commented out obsolete redirecting function: GetWindowContentRegionWidth().
727c462069 Internals: Added ImTextFindPreviousUtf8Codepoint() helper + comments.
c9d3c29aa3 Backend: Win32: support keyboard codepage conversion for when compiling in MBCS mode and creating a non-Unicode window. (#6785, #6782, #5725)
e3d9b875c9 ImDrawList: added PathEllipticalArcTo(), AddEllipse(), AddEllipseFilled(). (#2743)
fa2e5710ac MenuBar: Fixed an issue where layouting an item in the menu-bar would erroneously egister contents size. (#6789)
56a7b8b724 Backends: GLFW: Clear emscripten's MouseWheel callback before shutdown. (#6790, #6096, #4019)
d8ef864b02 InputTextMultiline: Fixed a crash pressing Down on last empty line of a multiline buffer. (#6783, #6000)
cf1c4a0cb1 BeginListBox(): fixed not consuming SetNextWindowXXX data when returning false.
fb9b006865 Version 1.90 WIP
f24387fa2b Version 1.89.9 (fixed changelog header)
c4dc8fd101 Docs: Update FONTS.md (#6781, #6632, #6339, #5763, #5330, #2234, #2042, #1259, #951, #220)
c6e0284ac5 Fixed minor warning.
fef3389157 Version 1.89.9
7b5fb33296 Tables: Internals: renamed CellPaddingY to RowCellPaddingY.
bdc4dfebf5 Tables: Amend support for cross-cell SameLine() for first column. Amend 9a93fb5 + 8f5ce73.
9a93fb5716 Tables: Fixed support for cross-cell SameLine() by preserving Line Y1 position. Amend 8f5ce73.
e8a5c9e1b8 Tables: Made it possible to change style.CellPadding.y between rows. Added demo.
3816d478df ImDrawList: small debug-mode optimization when calling AddRect() without rounding + Selectable: small debug-mode optimization.
8c497793f9 Demo: Tweak table sorting demo code.
303dc091b4 Revert "IO: ImGuiMod_Shortcut (Ctrl/Cmd depending on platform) is reflected in io.KeyMods. (#5923, #456)"
b8f93a8fe0 IO: ImGuiMod_Shortcut (Ctrl/Cmd depending on platform) is reflected in io.KeyMods. (#5923, #456)
a066074054 Clipper: Fixed a bug if attempt to force-include a range which matches an already included range. (#3841)
bdd9b96fa3 Demo: Reorganized "Examples" menu. Tweak Property Editor.
08b3a1a34a ImDrawList: Automatically calling ChannelsMerge() if not done after a split.
f93d0befaf Slider: fixed support for ImGuiItemFlags_ReadOnly/ImGuiSliderFlags_ReadOnly although it is technically unused/undocumented. (#6758)
d6e9fad60e Tables: Fix typo in TableGetSortSpecs comment + amend comment. (#6755)
82d177ccbd Using range-based for where it makes sense. (#4537)
33ea1e8b78 ColorEdit, ColorPicker: Manipulating options popup don't mark item as edited. (#6722)
f617fe7890 Clipper: Renamed IncludeRangeByIndices()/ForceDisplayRangeByIndices() to IncludeItemsByIndex(). (#6424, #3841)
200053771a Clipper: Added IncludeIndex() helper to include a single item. (#6424, #3841)
4a7810e992 Update README.md
981abb4612 Examples: Emscripten+webgpu: Fixed WGPUInstance creation process + use preferred framebuffer format. (#6640, #6748)
4a81424492 CloseButton, CollapseButton: don't include FramePadding into size. Use ItemInnerSpacing.x between title bar buttons. (#6749)
b41811a68c CollapseButton: handle clipping better + align circle like in docking branch.
040e818d72 Fix typo in SameLine comment (#6745)
162f8e01aa Backends: SDL3: fixed typo in 1e17d59 (#6735)
ba1fa904a9 IO: Exposed io.PlatformLocaleDecimalPoint to configure decimal point ('.' or ','). (#6719, #2278)
a1a7a1bc03 InputFloat, SliderFloat, DragFloat: always turn both '.' and ',' into the current decimal point character. (#6719, #2278)
a6857ede03 IO: fix writing to incorrect union section for MouseSource in AddMousePosEvent and AddMouseButtonEvent. (#6727, #2702)
c06c796242 TreeNode: added note about ImGuiTreeNodeFlags_Bullet.
1e17d59965 Backends: SDL2,SDL3: added ImGui_ImplSDL2_InitForOther() / ImGui_ImplSDL3_InitForOther().
983b95bc87 Demo: tweak explanation about static keyword. (#6718)
d2c7cbcbf9 Misc: Made multiple calls to Render() during the same frame early out faster.
ac64b65634 Render: draw dimmed background earlier to match docking code. (#6716)
8f5ce73140 Tables: Made it possible to use SameLine(0,0) after TableNextColumn() or TableSetColumnIndex() in order to reuse line height from previous cell.
1362fc0c56 Debug, Internals: Added DebugDrawCursorPos(), DebugDrawLineExtents() helpers.
bc3c0ce772 Nav, TreeNode: Pressing Left with ImGuiTreeNodeFlags_NavLeftJumpsBackHere now goes through proper navigation logic: honor scrolling and selection. (#1079, #1131)
edebb90a9a Demo: amend/fix for MinGW
4d6fbaff11 Demo: define standard PRI names we use (if missing) instead of defininig IM_PRId64, IM_PRIu64.
226923fa7e Metrics: Fixed "Drawlists" section and per-viewport equivalent appearing empty (regression from c649aca).
2b1fc6f765 Demo: Demonstrate out-of-order rendering using ImDrawListSplitter.
cf3726bcbc Internals: rename bg/fg drawlist holders in structs to reduce confusion.
52587c28d6 ImDrawList: Fixed OOB access in _CalcCircleAutoSegmentCount when passing excessively large radius to AddCircle(). (#6657, #5317)
f8c768760b Typo fix: _NoHostExtenY -> _NoHostExtendY (#6687)
c00e68102c Docs: update CONTRIBUTING.md
b7a7d673b9 Fixed an integer overflow and div-by-zero in SliderInt() when v_max is INT_MAX (#6675, #6679)
d6d94d90bf Version 1.89.9 WIP
f7eea63872 Version v1.89.8
ab490dc7b8 Fonts: Amends for support for OpenType SVG fonts using lunasvg (#6591, #6607)
2ad8c60abc Fonts: Added support for OpenType SVG fonts using lunasvg (#6591, #6607)
19ae142bdd Mark alternative ImColor constructors as constexpr, second attempt (#6656)
dc2b0a2823 Disable -Wreserved-identifier warning on Clang (applying to member fields seems excessively weird).
88a330ebef Revert "Mark alternative ImColor constructors as constexpr (#6656)"
fa2f1bfbb0 Examples: Vulkan: Fixed Vulkan descriptor pools. (#6642)
7c5b0e8292 Mark alternative ImColor constructors as constexpr (#6656)
eefc9035f0 Fonts: ImFontConfig::OversampleH now defaults to 2 instead of 3.
c87b9fdb15 Docs update. Improved Fonts troubleshooting section.
556ce9f543 imgui_stdlib: Fix warning (#6658)
79d6f4e211 Misc: Avoid stb_textedit.h reincluding string.h while in a namespace. (#6653, #4791)
f1781c20a3 Added IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION config macro to disable stb_sprintf implementation (#6626)
1109de3827 Tooltips: fixed ImGuiHoveredFlags_ForTooltip conflicting with ImGuiHoveredFlags_NoNavOverride since 10c7709f. (#6622, #1485)
db66e33e9e Tables: fixed GetContentRegionAvail().y report not taking account of lower cell padding or of using ImGuiTableFlags_NoHostExtendY. (#6619)
2bc5d17ac3 Tables: fixed calculation of multi-instance shared decoration/scrollbar width of scrolling tables. (#5920, #6619)
e5977f05d8 Backends: GLFW: revert support for GLFW_CURSOR_DISABLED, let user use ImGuiConfigFlags_NoMouse. (#5625, #6609)
d342ec10cc Overlap, IsItemHovered: clarification on using IsItemHovered() on a non-reactive item after a reactive overlappable one. (#6610)
52125a54a5 Tables: added TableGetHoveredRow() in imgui_internal.h. (#6250, #6347, #6588, #3740)
77eba4d0d1 CI: resume using latest Emscripten
3dc3aef8a4 Backends: WebGPU: fix webgpu changes for Dawn. (#6602, #6188)
0e8eb8c48e Removed _MSC_VER <= 1500 checks for Visual Studio 2008 or earlier.
863ac31f01 Doc: various tweaks + tweak imconfig comments.
33e13c85e1 Backends: Made all backends sources files support global IMGUI_DISABLE. (#6601)
6888e6cdff ImDrawData: call _PopUnusedDrawCmd() later. as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch). (#6406, #4879, #1878)
dbeeeae593 ImDrawData: Slight refactor so internal logic uses same logic as AddDrawList().  (#6406, #4879, #1878)
1a9ddd2396 ImDrawData: added AddDrawList() helper function. (#6406, #4879, #1878)
c649aca20a ImDrawData: changed CmdLists from raw array to ImVector<> owned by ImDrawData itself. Faclitate user-manipulation of the array (#6406, #4879, #1878) + deep swap. (#6597, #6475, #6167, #5776, #5109, #4763, #3515, #1860)
cc4c37dbac Nav: PageUp/PageDown always set NavJustMovedTo even when landing on same spot (which can trigger a selection).
b7cdb5a31e Scrollbar: layout needs to take account of window border size, so a border size will slightly reduce scrollbar size. (#2522)
5ce636b0ba Tweak to accomodate for build* directories anywhere.
140726d23f Fixed CTRL+Tab dimming background assert when target window has a callback in the last ImDrawCmd. (#4857, #5937)
b32ef809c3 InputText: Fixed a case where deactivation frame would write to underlying buffer or call CallbackResize although unnecessary, in a frame where the return value was false.
3349296370 InputText: Tweak ImGuiInputTextFlags_EscapeClearsAll handling so decision is taken on input buffer + Showcase a few more InputText() flags. (#5688, #2620)
6aa408c6af IO: Added io.ClearEventsQueue(). Obsoleted io.ClearInputCharacters(). (#4921)
9a15730c2a Demo: better showcase use of SetNextItemAllowOverlap(). (#6574, #6512, #3909, #517)
3fe4319314 Version 1.89.8 WIP

git-subtree-dir: external/imgui/imgui
git-subtree-split: d6cb3c923d28dcebb2d8d9605ccc7229ccef19eb
2024-01-17 20:29:18 +01:00
f701b7d2f8 Merge commit '852f2a6343518919e5ca8d3c1bbcab9f493e3cd8' 2024-01-17 17:02:59 +01:00
852f2a6343 Squashed 'external/sdl/SDL/' changes from 399bc709b7..0d7df16812
0d7df16812 Timers are a required platform feature
518b070aa9 Fixed drop event coordinate conversion
3ca8cee874 Sync SDL3 wiki -> header
8bf74280e0 docs: Note SDL_UpdateWindowSurfaceRects can update beyond specified areas.
9408299bad Set the number of audio devices to 0 if audio hasn't been initialized
be0ba78c56 Convert the coordinates in drop events as well (thanks @Dragon-Baroque!)
e6c8872fdc Fixed bug #7614: Segmentation Fault in SDL_BlitSurface
ce0e0675de Fixed bug #8301 - Software renderer draws long lines incorrectly / SDL_RENDERLINEMETHOD_GEOMETRY
6e48d4532c Added raw input device handle for WM_INPUT mouse button handling
cf5e0637b5 Touchpads already have their right/left buttons swapped in raw input
fb2d7ed83e Restore window minimum and maximum size if it is recreated
915afae514 Remove force using one thread
64a3e2d17d Remove ps2_driver from workflow
464a41411e Sync SDL3 wiki -> header
4f3d4bd110 wayland: Add the ability to import and wrap external surfaces
99f6bcf504 Sync SDL3 wiki -> header
3a4ac15a27 Make Xbox GDK code public (and fix some GDK code rot) (#8844)
649556befa win32: Let windows manage the floating state unless explicitly overridden
d4a9748740 win32: Fix test failures
101f903bb1 testgeometry: allow to use arrows to move the triangle
4033a0a83b Prevent ASAN warning: like SDL_triangle.c:305:30: runtime error: left shift of negative value -672 (even if the value was correctly computed)
f0b9c7f0f0 Let Windows track floating window state
98be55894e Moved window state change handling from WM_WINDOWPOSCHANGING to WM_WINDOWPOSCHANGED
7efeb36131 Pass the frame DPI to WIN_AdjustWindowRectForHWND()
e4ee1cade7 Revert "SDL_windowsevents.c: fixed -Werror=unused-variable"
f8cce4ead4 SDL_windowsevents.c: fixed -Werror=unused-variable
277fded7ba Refactor AdjustWindowRectEx() into WIN_AdjustWindowRectForHWND()
312160935d Removed deprecated use of __IPHONEOS__ (thanks @Dragon-Baroque!)
483155bbf9 Fixed borderless window client area calculation
cf0d1d7234 Fixed ASAN warning, runtime error: left shift of 160 by 24 places cannot be represented in type 'int'
0c6b070761 Capture debug macros off by default
b5bc64aa55 Fixed pause key on Windows (thanks Mikhail!)
d766f68cb3 Fix compile error with XInputOnGameInput.h
ec2159d492 tests: Clean up the window creation properties in the Wayland custom surface example
9a77813df0 WinMain is specified by the Win32 API as having extern "C" linkage
6407e0cc37 Added attribution for GIP protocol handling (thanks @medusalix!)
3a219caf3d wayland: Restore accidentally removed line of code
ccae9c1ef6 Only initialize audio drivers that have been requested
590d0bec6f Revert "Make sure new windows have mouse and keyboard focus if there's no windowing system"
5948ea997f Make sure new windows have mouse and keyboard focus if there's no windowing system
05d18aab1c Sync SDL3 wiki -> header
4b6df89238 wayland: Add a property to allow creation of a wl_egl_window object even if OpenGL is not enabled
f7dd0f9491 wayland: Allow the creation of roleless window surfaces for custom application use
4417250d0d wayland: Remove the registry from the window properties
392796e49c wayland: Eliminate redundant protocol checks
82f2c4d581 render: Renamed SDL_GetTextureRenderer to SDL_GetRendererFromTexture.
95066ce2a0 Sync SDL3 wiki -> header
df438a3170 opengl: OpenGL renderers need to support texture target in properties.
8e7c0b34d7 test: If SDL_CreateRenderer() fails, say why
022ff075b9 test: When listing test-cases, say which ones are disabled
44adab7dfd Sync SDL3 wiki -> header
059fb560ba gamepad: Clarify range and direction of axes
4942027117 Sync SDL3 wiki -> header
e056f52f7d include: Remove string literals from properties documentation.
e8c595af5c Sync SDL3 wiki -> header
adef35b9ec include: Attempt to make new properties documentation wiki bridge friendly.
4ffec098b7 Sync SDL3 wiki -> header
9bc7cfc755 render: Added SDL_GetTextureRenderer().
7eae08cfc4 Removed SDL_GL_BindTexture() and SDL_GL_UnbindTexture()
4d5bffc323 Sync SDL3 wiki -> header
1a13dae219 Added constant definitions for SDL properties
3deefa6b43 Updated documentation for SDL_HINT_SHUTDOWN_DBUS_ON_QUIT
2348e8b6a2 Add hint to make SDL handle dbus_shutdown()
d3daba791a Don't try to send the PS third-party feature request to the Logitech G815 keyboard
014a63b4b5 Renamed ShowTextInput/HideTextInput to ShowScreenKeyboard/HideScreenKeyboard on Android
3a9a52fe6c updateKeyboard should use the SDL window's screen instead of the view window's screen, which may be nil.
e100992c17 Added mapping for the RX Gamepad, e.g. Pandora Box 7
139a0931a3 Fix memory barriers on ARMv5
21c80ac843 Added a practical example of confirm/cancel gamepad button handling
787a15f760 Fix Mac child windows that are created hidden showing if their parent window is shown
d6b1fc9576 Add SDL_MAC_REGISTER_ACTIVATION_HANDLERS hint to allow disabling the normal application startup/activation/deactivation handlers
7c5e694022 Ignore a new warning in Visual Studio 2022
73d02184d7 docs: Add Wayland to the SysWM migration example
c03c01e9b2 Make sure we get mouse events as soon as possible
5cbdeab799 Rename SDL_mslibc_x64.asm -> SDL_mslibc_x64.masm
ed62d6e7de cocoa: Set the titled flag on fullscreen space windows
b4b5dbd92f testcontroller.c: fixed warnings.
31851a50d2 Fixed dropping raw input because of mixing GetRawInputBuffer() and WM_INPUT handling
987744aae8 Fix Duplicated includes
2b369a14ab Fixed allocation and alignment of raw input buffers
bec1b8f849 Add basic rumble support to Steam Deck
8fe4a45edf Use GetRawInputBuffer() instead processing raw input one at a time
87b8f09657 Fixed warning: no previous prototype for function 'SDL_PrivateGetGamepadButtonFromString' [-Wmissing-prototypes]
c2951655ff Fixed warning: missing field 'window' initializer [-Wmissing-field-initializers]
5b3ee51c6c Updated copyright for 2024
a7b79c483c Remove unused 'window' variable from -[Cocoa_WindowListener windowWillExitFullScreen]
dd2d809407 AndroidShowToast: make OneShotTask members private final
44c2f344d6 Fixed build
2faae8457d The C standard defines a boolean expression as a signed integer value.
e3d50619f8 Fixed fatal error: SDL_pen.h: No such file or directory
dfe1a37bab Fixed error: 'static' is not at beginning of declaration [-Werror=old-style-declaration]
423b1fafcd Fixed warning C4047: 'function': '__x_ABI_CWindows_CGaming_CInput_CIRawGameController **' differs in levels of indirection from '__x_ABI_CWindows_CGaming_CInput_CIRawGameController *'
7681695875 Revert "Fixed signed/unsigned warnings with Visual Studio when comparing SDL_bool with boolean expressions"
8f94102b04 tests: Use unsigned format specifiers for printing flags
5d0c893723 wayland: Remove bitfield specifiers from boolean values
530b41d531 Fixed warnings in SDL_pen.c
9906d6d3bc Fixed warning C4244: '=': conversion from 'SDL_bool' to 'Uint8', possible loss of data
ebd7f9adbd Fixed warning C4245: 'initializing': conversion from 'int' to 'Uint32', signed/unsigned mismatch in SDL_video.c
dc1c27885e Fixed warning C4389: '!=': signed/unsigned mismatch in SDL_blit.c
e813c72b3c Fixed warning C4245: 'return': conversion from 'int' to 'SDL_JoystickID', signed/unsigned mismatch
dce626f469 Fixed warning C4244: 'function': conversion from 'int' to 'Uint16', possible loss of data
7f376277e5 Fixed warning C4244: 'initializing': conversion from 'SDL_bool' to 'Uint8', possible loss of data
61db102da9 Fixed signed/unsigned warnings with Visual Studio when comparing SDL_bool with boolean expressions
d71454da17 Store the surface properties in the reserved pointer of a surface
b6a92c113f wayland: Don't apply old libdecor window dimensions
39e24e52c8 Fixed example of creating a window with properties
dc450ba908 Added an example of creating a window with position
ce4fe32ce3 Added documentation for getting the X11 display from an SDL window
327d31a5d9 Added documentation for getting the NSWindow from an SDL window
3976bbef2a Added documentation for getting the X11 window from an SDL window
ffb8515c21 Use the Valve code name for the Steam Deck controller
43c40d30a2 Added comment for the BDA Pro Ex controller
61704b5862 Removed an assertion it's possible to hit
c24b33d8d9 Fixed building with older Windows SDK
70ba3f2830 Report the D-Pad for HIDAPI gamepads as a hat
ce329d60e4 Added support for alpha blending using palette alpha
9c3e831e33 uikit: Send fullscreen enter/leave events
5df3eac925 Sync SDL3 wiki -> header
0dfdf1f3f2 Fixed crash if joystick functions are passed a NULL joystick
4ce935b910 Fix static build with libdecor 0.2.0
5d6d149862 Allow passing in `extrainfo` value to `GetMouseMessageSource()`
e0df963ef0 Fix wrong bit count in comment
c2a55cd2c5 Add missing `(void)` in functions params
a3c8f2f6cb Consolidate mouse-touch event checking logic
d747daf03d Use correct touch check flag
a961066d0b Add basic touch/finger support to `testpen.c`
a3b5eb07b2 Removed extern "C" linkage from main() declaration
50e309bb17 Include SDL_events.h in SDL_main.h
cae657140c Add Access Controller
74418e1aa8 Made the cursor list check a compile time assert instead of a runtime one
d6fb0d91d8 Added testpen to the Visual Studio solution
bbdd41f287 Fix windows touch using wrong axis for normalisation
a28ac29aa0 Add missing cursor types
6daf2e943f Try SDL_UDEV_deviceclass to detect joysticks even if in a container
1bf78ed544 We get a resize event when the view enters fullscreen mode on iOS
e3b5999bb4 Use the application requested size to determine automatic orientation on iOS
278e3f9184 Whoops, fixed setting fullscreen flag
69e60e0f1b Fixed setting fullscreen mode on iOS
0e5ea3cc4b Fixed infinite recursion when adding an accelerometer as joystick on iOS
c3d84c3342 Record the initial input report mode and only restore simple mode if that's what we started with
10a8b750a0 Use common generic syscond for platforms with no cond implementation
4914e5bb78 PS2 use WaitSemaEx for waiting for semaphore with timeout
bb0e0ae080 Added a runtime check for BLUETOOTH_CONNECT in addition to BLUETOOTH (thanks @perepujal!)
312f98c2a1 Make sure the string is terminated, fixed invalid read in SDL_PrivateParseGamepadConfigString()
199f7cc3b1 x11: Ignore border extents when the border hint is unset
07e9603398 Sync SDL3 wiki -> header
ffd82fb7c4 Add scaleMode to SDL_SoftStretch(), remove SDL_SoftStretchLinear().
5dba04b29b Remove SDL_{Set,Get}SurfaceScale(). Add Scale parameter to SDL_BlitSurfaceScaled() and SDL_BlitSurfaceScaledUnchecked() (see #8732)
e66896406d cocoa: Set appropriate flags on fullscreen spaces windows
57fcb9044c video: Remove more assumptions about window state in the video layer
cb90653695 win32: use USER_DEFAULT_SCREEN_DPI instead of explicit 96 value
a2e05480d6 Use crc16 return value when calculating GUID
2ad50e9675 Make the SDL3 surface ABI compatible with SDL2
f72d6a7fd9 Use more verbose names for properties, to match upcoming public property names
1f1ee6f77c Use the original manufacturer and product strings for the joystick CRC
4bb5e1f0f9 Added migration notes for migrating Steam Input support from SDL2 to SDL3
56f111dffc Fix compilation / same as sdl2-compat
72c366bf3d Fixed whitespace
3152b98e87 win32: minor fixup in WIN_UpdateKeymap()
2c4360ce8f Sync SDL3 wiki -> header
c981a597dc Added Steam Input API support for game controllers
a8f4f40d08 Sync more Steam Controller header definitions for the Steam Deck
e6e54b22c8 Ignore all surface comparison output files in the testautomation directory
9d13be743b Make sure we're rendering whenever the activity might be visible, even if we don't have focus.
69ec0322d3 win32: Make leaving fullscreen when the window already isn't fullscreen a no-op
8f79e0b7f8 win32: Fix high-DPI debug logging
a4496f7dcf Update doc: SDL_SoftStretch() and SDL_SoftStretchLinear() #8667
43309d38ed joystick: Extract 0x02a9 and 0x0291 PIDs into separate defines.
84a0d5f623 Added SDL_SetSurfaceScaleMode() and SDL_GetSurfaceScaleMode() to control scale mode using SDL_BlitSurfaceScaled()
4d5949dcf6 Added a controller name for the Steam virtual gamepad
695846f2ed Pass through the name of the controller for the XInput mapping
1745289b1b x11: Don't move the window when restoring and ensure that resize and position events are sent when entering or leaving fullscreen
08a7ca4d53 XInput: Use XInputGetCapabilitiesEx instead of fragile GuessXInputDevice
7f75178908 Verify that the %p format specifier works for 64-bit pointers
72b7acfe8a Don't create a TLS error buffer if we're just clearing the error
240e7747c8 Fix #8702: Add SDL_hidapi_steamdeck.c to Xcode target
b937c54b66 win32: Set all size and position variables for external windows
8c285828e5 Fixed return value for SDL_UDEV_AddCallback()
cbf9012c74 Fixed build
5547007915 Added test for inverted cursor
0ab13f2498 joystick: fixup for Wireless Xbox 360 Controller VID/PID detection in WGI backend.
627d134b9e Add support for monochrome cursors with inverted pixels under Windows.
21879faf48 wayland: Handle mouse focus when receiving touch events
58a5f5cbe8 Allow sendCommand() to be overridden by derived classes
e6d8ef1a5b Revert "Back out Steam virtual gamepad changes"
b0e7b7db6f Don't unload graphics libraries until after the window has been destroyed.
69288038ed Refactor away some additional integer types.
f3048e3cd2 Add new file to vcxproj files.
5a21febecb Add new steam deck HIDAPI controller to controller database.
67d44c1017 Disable lizard mode while steam deck HID device is opened.
6dd6827343 Translate steam deck HID reports to SDL events.
94f621e030 Implement steam deck HIDAPI initialization.
c1a7d0f96e Add steam deck detection and HIDAPI driver scaffold.
0baee3e676 Reversed test to be easier to read, more efficient, and match other code
bddbd1e317 cocoa: Check the resizable flag along with zoomed status when resizing
835c733f4d video: Only sync when programmatically entering/exiting fullscreen
c790572674 Use existing XUSB driver software PID 0x02a1 instead of PID 0x02fe
581d1cab25 You should call present when using a software renderer as well.
5173b0c2cc Make built-in joystick device lists extendable by using hints
34eb24ebc4 Back out Steam virtual gamepad changes
7529d25b2b Use the Steam virtual gamepad slot as the gamepad player index
445f08a0fb Print the gamepad player index when opening a gamepad
17723381da Sort Steam virtual gamepads by Steam controller slot
f3d8a2def5 audio: Fixed resource leak in unlikely failure case during device add.
63ae84e140 x11: Improve sync algorithm
7e5511d3cd x11: Move unrelated variables out of XFixes #ifdef
3c5e9e6112 We can wait up to 500ms in onNativeSurfaceDestroyed(), so wait at least that long in onDestroy()
53cda988f4 Clear any errors from a previous run
a197efe3a7 pen: fix leak caused by pen subsystem
fbb0914b78 wayland+x11: free system cursors when quiting video
7484d02a2e testshape: use SDL_test to create multiple windows
20250aecc5 Sync SDL3 wiki -> header
4fd778119b video: Implement asynchronous windowing
ace385a134 Revert "Fixed warning C33010: Unchecked lower bound for enum scancode used as index."
e482f00a17 SDL_string.c (SDL_vsscanf): fix gcc build
f00ecf5f19 Fixed building with older Windows SDK
7ca43995a1 Fixed warning C4028: formal parameter 1 different from declaration
aab7432f5f Fixed analyze warnings for SDL_dynapi_procs.h
c484140f56 Fixed warning C33010: Unchecked lower bound for enum scancode used as index.
02a116217d Fixed Arithmetic overflow: Using operator '-' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator '-' to avoid overflow (io.2).
91da942b33 Fixed warning C28251: Inconsistent annotation for 'SDL_RWvprintf_REAL': this instance has no annotations.
b8840801cc Fixed analyze warnings in SDL_xinputhaptic.c
8e0d728c67 Fixed warning C26451: Arithmetic overflow: Using operator '-' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator '-' to avoid overflow (io.2).
6a736d7766 Fixed warning C6340: Mismatch on sign: 'unsigned char' passed as _Param_(2) when some signed type is required in call to 'SDL_SetError_REAL'.
1fcc75ba81 Fixed warning C6340: Mismatch on sign: 'unsigned char' passed as _Param_(4) when some signed type is required in call to 'SDL_snprintf_REAL'.
7f2e16db8b Fixed warning C6340: Mismatch on sign: 'const unsigned short' passed as _Param_(2) when some signed type is required in call to 'SDL_SetError_REAL'.
163de8e697 Fixed warning C6001: Using uninitialized memory 'rdi'.
e29393e407 Fixed warning C6001: Using uninitialized memory 'devName'.
0f34ca2e71 Fixed analyze warnings in SDL_xinputjoystick.c
2b5c7db645 Fixed analyze warnings in SDL_render_d3d12.c
a28769759b Fixed warning C26052: Potentially unconstrained access using expression '(signed char *)info'
6ee34380f4 Fixed warning C6011: Dereferencing NULL pointer 'viewport'.
230581f4a8 Fixed warning C26451: Arithmetic overflow: Using operator '+' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator '+' to avoid overflow (io.2).
3775d9be4b Fixed warning C28251: Inconsistent annotation for 'SDL_LogMessageV_REAL': this instance has no annotations.
b512182222 Fixed warning C6340: Mismatch on sign: 'unsigned int' passed as _Param_(2) when some signed type is required in call to 'SDL_SetError_REAL'.
8a82f7e837 Fixed warning C33005: VARIANT '&valueX' was provided as an _In_ or _InOut_ parameter but was not initialized
fda039e6f8 Fixed analyzer warnings for SDL_string.c
22f44aefe7 Fixed warning C6340: Mismatch on sign: 'int' passed as _Param_(3) when some unsigned type is required in call to 'SDL_sscanf_REAL'.
eab2d97d07 Fixed warning C26451: Arithmetic overflow: Using operator '*' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator '*' to avoid overflow (io.2).
41bfcad5d7 Fixed warning C6340: Mismatch on sign: 'unsigned short' passed as _Param_(2) when some signed type is required in call to 'SDL_SetError_REAL'.
54dc73aa88 Fixed warning C6386: Buffer overrun while writing to 'palette_saved_alpha':  the writable size is 'sizeof(Uint8)*((palette_saved_alpha_ncolors))' bytes, but '2' bytes might be written.
0dad56354c Fixed warning C6326: Potential comparison of a constant with another constant.
c9b243fb56 Fixed warning C6263: Using _alloca in a loop: this can quickly overflow stack.
4ccc53edfe Fixed warning C6011: Dereferencing NULL pointer 'display'.
3db4695ac7 warning C6340: Mismatch on sign: 'unsigned int' passed as _Param_(3) when some signed type is required in call to 'SDL_LogDebug_REAL'.
c7d81d936a Fixed warning C6031: Return value ignored: 'GetKeyboardState'.
69b9d44bdc Fixed warning C26451: Arithmetic overflow: Using operator '-' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator '-' to avoid overflow (io.2).
a9b87ee201 Fixed warning C28159: Consider using 'GetTickCount64' instead of 'GetTickCount'. Reason: GetTickCount overflows roughly every 49 days.  Code that does not take that into account can loop indefinitely.  GetTickCount64 operates on 64 bit values and does not have that problem
21f273ecc7 Fixed warning C6255: _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead.
0c4cb3d153 Fixed warning C26451: Arithmetic overflow: Using operator '<<' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator '<<' to avoid overflow (io.2).
06f8f9a891 Fixed warning C6326: Potential comparison of a constant with another constant.
3e54061fa8 Fixed warning C6011: Dereferencing NULL pointer 'SDL_disabled_events[hi]'.
226f8fde09 Fixed warning C28182: Dereferencing NULL pointer. 'streams[j]' contains the same NULL value as 'stream' did.
fe6b3ab0b0 Fixed warning C6031: Return value ignored: 'CLSIDFromString'.
f3b0149756 Fixed warning C26451: Arithmetic overflow: Using operator '*' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator '*' to avoid overflow (io.2).
6cfce101fb Don't call the property cleanup function if setting the property fails
14380ec48a Fixed signed/unsigned comparison warning
ac0751a652 Added SDL_strnstr()
7c71e72193 SDL_render: Call InvalidateCachedState to initialise some of driverdata values (eg '*_dirty' to 1). At the earliest place, immediatly after driverdata is set. (Doing it in SDL_render.c, after creation, would be too late, because there're renderers that already use/change those values in the CreateRender() function).
058213366b Make sure we use alpha of 0 when clearing a transparent window with no content
ecd56bb8f0 Removed SDL_GetErrorMsg(), trivially implemented in application code
66e532fa61 Handle out of memory errors without any allocation
579681a372 fixed SDL_SCANCODE_LEFT array position in scancodes_windows.h
675423f096 Sync SDL3 wiki -> header
744a29b88f WGI: Cleanup code a bit (#8647)
e4582e6edc Sync SDL3 wiki -> header
df0fd55891 wikiheaders.pl: README files are no longer in Windows endline format.
1072b6e86e docs: fixed typo
f184dea16c uikit: Patched to compile.
447b508a77 error: SDL's allocators now call SDL_OutOfMemory on error.
70b65d4170 cocoa: Resync modifier keypressed on NSEventTypeFlagsChanged event.
d1b831e232 include: Clarified documentation for two functions.
daa38dc793 touch: Replace GetNumTouchDevices/GetTouchDevice with a single function.
dd47da8a5c gamepad: Replace GetNumMappings/GetMappingByIndex with a single function.
dfee3f9e92 render: Replaced SDL_RenderFlush with SDL_FlushRenderer.
eef5c53668 docs: Moved CREDITS and INSTALL to markdown format.
f32575dfab docs: Convert everything to Unix-style endlines.
c5daf8adb1 testcontroller: Don't query the mapping list until after they are available.
df05d5eff4 Fix scroll wheel handling in testmouse
63d4bd4e57 SDL_SendKeyboardText: remove workaround from 1e12d7c, fix use of iscntrl
fea6e7afb1 cmake: copy sources to binary directory in separate target
9faa7977bd UWP: Add support for supplementary Unicode characters input
7b628ea4d5 Win32: Simplify Unicode text input code
411c70abb1 Fix the target membership of SDL_pen.h (thanks kanjitalk755!)
3e6513c773 document the purpose of SDL_SetTextInputRect
1e12d7cfb6 Make sure we don't pass UTF-8 to SDL_iscntrl()
9a206adbee UWP: Use Windows.UI.Core.CoreDispatcher.AcceleratorKeyActivated event for keyboard
309ea2d5f9 UWP: Simplify Win32 scan code to SDL scan code mapping
acc5bb89f8 [Win32] Better keyboard button mapping to SDL scan codes
6b28065e9e Reformat Win32 scan code table
7e86b6aef2 Win32: Fix keymap for keyboard layouts that can print UTF-16 surrogates and ligatures
08c6ac1b16 test: SDLTest_PrintEvent now reports key event mod state.
571e9796b9 Fixed the GameCube HIDAPI controller mapping
dd984dcd9f Removed HIDAPI controller mappings
3817f5126e cocoa: Use `-[NSApplicationDelegate applicationSupportsSecureRestorableState]`.
c5b0ff77d7 test: testwm2 now displays keyboard mod state.
5772e00c3f cmake: disable oss by default on OpenBSD, not FreeBSD
ceac93ade2 Set framebuffer_srgb_capable to the actual value obtained (#8634)
ed3fad1880 cmake: disable oss by default on Linux, NetBSD and FreeBSD
6bb40f1d8d SDL_VideoCapture: allow add/remove device at runtime on linux
f0e47f8ee0 Added support for the NACON Revolution 5 Pro controller
2f806c89b5 initial import of hidapi netbsd uhid native backend from mainstream
1b284cd415 X11 pen detection: fix misclassification due to improper init
c4ca64deaf ci: do 'brew update' & don't run dependent checks on installed things
d486de6349 cmake: fixed iconv detection test program
42a8139fd6 render: Clip bresenham lines against a real viewport thing.
4a40a272bd render: Patched to compile.
983f178b7d render: Clip lines before Bresenham algorithm generates points.
db7f6425d0 rect: Avoid numeric overflow on massive lines in SDL_IntersectRectAndLine.
e548044a82 ci: add NetBSD to test matrix
dd5b8db3a6 SDL_hidapi requires libusb >= 1.0.16
6ba90f7775 render: Batching is always enabled now!
b24d6bd59a opengl: Creating a texture trashes the cached `texturing` state, fix it.
dcf04559db render: GL-based renderers should treat adaptive vsync as vsync being enabled.
74a2542564 x11: Deal with difference in GLX_EXT_swap_control_tear behavior.
08fac5b1b2 SDL_PenModifyForWacomID: return zero as axis_flags upon failure.
aaba01aee4 hidapi: syncing with mainstream:
5730eb67f0 add HAVE_GCC_DIAGNOSTIC_PRAGMA to SDL_internal.h, use it
42c8366fdc revise iconv detection:
a45b371de0 cmake: create and install SDL3::Jar target for Android
53544cabaa psp: fix copy/paste error from SDL2 in PSP_VideoQuit().
6cde96f9a0 psp: Hook up event subsystem init/quit.
e9659f5738 Removed some function declarations that don't actually exist.
3264e64738 SDL_RunApp: Make sure argc/argv are stable if the caller didn't define them.
eca79e38db Removed test shape images
81fc7ded78 Removed the window shape API for SDL 3.0
45938bbfa5 Corrected comment
059e550e98 Fix 3DS Analog Values (#8581)
dbf14df80f Fix joysticks returning invalid device IDs
d2db3f3993 ci: cache android ndk archive
d6291d4d42 alloca: use alloca from <stdlib.h> on NetBSD
f26a93211f SDL_bsdjoystick: fix -Wundef warning on FreeBSD
d1def7f033 cmake: add openbsd wscons sources to build, if supported
86d77bbcc5 kmsdrm: restrict KMSDRM_ReleaseVT/KMSDRM_AcquireVT to SDL_INPUT_LINUXEV
94ad1a4ae4 SDL_bsdjoystick.c: fix -Wundef warning.
e761770c24 No, this wasn't right
b7d7e54895 Fixed uninitialized variable
119e02f314 Always use 'm' for controllers provided by the MFI joystick driver
75df4cc5c2 Don't tickle PS4 Bluetooth controllers in simple mode
0d431015bf Extract BMP magic into separate define
0413f6fc49 Use SDL_iscntrl() call instead of manual code that is doing the same (#8593)
fd91178d7f Make size cursors be double arrows on Wayland
5e9b0820f3 Add cursors for X11/Wayland window resizing
91e122316c Use SDL_small_alloc() instead of SDL_stack_alloc() in CreateMaskBitmap()
b76f8de298 Hit testing tweaks for X11 and Wayland (#8582)
5b1c68c2f3 testshader: Don't make local variables with the same name as GL entry points.
30a2291d59 Fixed compatibility with sdl2-compat (thanks @sezero!)
49d58bc73a Cleanup WIN_CreateCursor() code a bit
4722269fb6 tests: Print window occluded log events
5db781cc3d Use the correct pixel formats for OpenGL ES on big endian
05e7dcf8f8 Support returning <8bpp surfaces in SDL_LoadBMP_RW
773ec1cfcb Extend blitting support for all <8bpp formats
753bbd199e Add SDL_PIXELFORMAT_INDEX2LSB and SDL_PIXELFORMAT_INDEX2MSB
fda69e5e79 Test both MSB and LSB indexed surfaces in testautomation
39870031d1 use format string attributes for functions accepting va_list params, too
666301f9f9 Fixed build with older macOS SDK
238987df3b Always use physicalInputProfile on OS versions that support it
e424dcca4b More fixing the build with older macOS SDKs
4ebb0c6dac Revert "Fixed build"
7abacc9f9f Fixed build
ac1f896f89 Fixed building with older macOS SDK
8043dad369 Fixed build warning
5c8c3931f2 Removed outdated information about SDL_GetWindowWMInfo()
e0d0d140b2 Fixed SDL_GetWindowWMInfo() code example
f61c0f3dc1 SDL_HINT_TV_REMOTE_AS_JOYSTICK should only affect Siri Remotes on Apple TV
51fc134cad Added support for the second generation Siri Remote on Apple TV
bfba9de43a Revert "Map Siri Remote touchpad to D-Pad for consistency with physicalInputProfile mapping"
f2c12fe5f3 Removed TODO.txt
d4448fe3d2 Update virtual joystick test now that we're just using abxy for mappings, for compatibility
f40f272107 Fixed build
34bdd321bf Improved navigation while setting up controller mapping
57e5c7f6ee We'll just use the legacy names for face buttons in the mappings
051ed397d1 Removed testautomation_syswm.c from the Xcode project
fb08c22abf Disable controller state debug messages
2999634f29 Map Siri Remote touchpad to D-Pad for consistency with physicalInputProfile mapping
2ff9255f29 Print the GUID for controllers that don't have a mapping
64939d3586 Switch the menu button on the Apple TV remote to the B button to match UI guidelines
0fe5713964 Improved GCController handling on Apple platforms
aaf54b09a1 Revert "Cleanup WIN_CreateCursor() code a bit"
e923a458ea audio: Protect against race conditions when closing a physical device.
8fa0746d4a audio: Fix postmix state when migrating to new default devices.
078995bbe0 x11: Set the skip taskbar and skip pager atoms on utility windows
a58af6d0d1 wasapi: Reference devices when proxying to management thread.
34392db9c3 Fixed IOS_SupportedHIDDevice() returning SDL_FALSE before initialization
708f18d49e Added SDL_HINT_JOYSTICK_IOKIT and SDL_HINT_JOYSTICK_MFI to control whether the IOKit and GCController drivers should be used for joystick support.
bd4966999b Replacing SDL_SCANCODE_AUDIOMUTE by SDL_SCANCODE_MUTE on Windows
15504da0b8 Fixed mouse wheel scrolling direction on iOS
f5600fd9f4 Fall back to using the physical profile for Apple controllers if they don't match a standard profile
924de4df48 Enable transparent windows when using the D3D11 renderer
1c64366b80 Added SDL_CreateRendererWithProperties() and SDL_CreateTextureWithProperties()
7203641597 Note that the SDL window properties are read-only
e0c45c6c98 Renamed SDL_WINDOW_FOREIGN to SDL_WINDOW_EXTERNAL
229b7b9d50 SDL_CreateWindowWithPosition() and SDL_CreateWindowFrom() have been replaced with SDL_CreateWindowWithProperties()
2c1fbe1967 Revert "Check to make sure the Windows joystick device has buttons and axes"
edd044e901 Fixed the ROG PUGIO II showing up as a game controller
861b1ebd12 properties: Use a mutex instead of an RWLock to guard the hash table
dd8ab67bd9 Sync wiki -> headers.
91460fc13d include: Fixed up documentation in SDL_pen.h
876c97454a Cleanup WIN_CreateCursor() code a bit
8766aa39d6 Sync wiki -> headers.
e5ffd6d8eb include: Removed `\link` and `\endlink` Doxygen tags.
1c4723ac66 SDL_CreateWindowFrom() now takes a set of properties that describe the native window and options.
6afae6681b Allow casting properties of different types
bd269b0f41 Added SDL_SetBooleanProperty() and SDL_GetBooleanProperty()
c47ac5b2df include: Fixed copy/paste error
0efb3d90e0 audio: removed a fixed FIXME comment.
69cae07cc1 cpuinfo: Fix detection of physical memory above 2GB on NetBSD
45fc828c95 move SDL_EVENT_WINDOW_PEN_ENTER and SDL_EVENT_WINDOW_PEN_LEAVE down
0907f345cb Added property types: pointer, string, number, float
7c80ac6df7 API for pressure-sensitive pens + XInput2/Wayland
d3e43668d0 Revert "Sync SDL3 wiki -> header"
43571769f1 Sync SDL3 wiki -> header
843873626c Handle window resizing on the render thread in D3D11 and D3D12
f66f61de01 Fixed missing "0" in the button list
04b6b2979f Re-add SDL_assert() with non boolean ptr syntax (#8530)
b374105975 Replaced SDL_GetTextureDXGIResource() with texture properties
09d1e9defb Only update the battery status for Bluetooth Switch Pro controllers
87794d03ad Added shortened name for "Nintendo Co., Ltd."
fbb6934905 Added support for the Dragonrise GameCube adapter with VID 0x1843
d98e1bdfe1 Use the standard gamepad type for Switch Pro controllers using the GameCube form factor
a5a47d3bee Fixed crash if there is no controller mapping
312faf9305 Updated documentation for the controller face buttons
2991b9f6ac SDL now represents gamepad buttons as positional elements with a separate label
8708ba7393 Don't leak if realloc fails
36b2d2e463 Fix memory leak in SDL_SendDrop()
dfb87e1099 Fix uninitialised variable 'properties'
e54c5e0204 Fix condition in SDLTest_TrackAllocations()
89408a9705 wasapi: ResetWasapiDevice no longer blocks on management thread.
aa7baf63aa Sync wiki -> headers.
74f3643bfa wayland: Add missing break to switch statement
2d6bae70b4 Older gcc does not support #pragma GCC diagnostic inside functions
d8600f717e  Pointer as bool (libsdl-org#7214)
23db971681 x11: Ignore deprecated declaration of XKeycodeToKeysym
61c39ce848 ci: re-enable Intel compilers on ci
4ac3f5c07e Updated Xcode project with the video capture API
59f93e20a7 Add SDL Video Capture, with back-end for linux/macos/ios/android
3ab98a3572 Removed debug print statements (thanks @stsp!)
f4b61fff30 Implemented VT switching for KMSDRM on Linux
391a3d23d0 cmake: the compile-time pdb does not have a suffix, if set
a6541166bc cmake: also install pdb files of static libraries
2e3f574f8f cmake: don't add the C runtime library to the .obj file (when using MSVC)
bea34c5380 Fixed a memory leak at window creation.
b5347c3364 Fixed emscripten and iOS builds
02f356439d Allow the application to draw while Windows is in a modal move/resize loop
1934417b4d Show the existing mapping when a controller is connected
407a3cb4e0 Fixed infinite recursion initializing properties
979214363f Added SDL_GetGlobalProperties()
151cdfa99f Added the "SDL.window.wayland.registry" property
6c91b28e71 Added the "SDL.renderer.d3d12.command_queue" property
4e8d1ec983 Fixed crash trying to create a metal view with the dummy driver
fd4a2cce9e SDL_syswm.h has been removed and replaced with window properties
aea6e6de6f Simplified SDL_SetProperty() and added SDL_SetPropertyWithCleanup()
a1941fad6c Replaced SDL_RenderGetD3D11Device(), SDL_RenderGetD3D12Device(), and SDL_RenderGetD3D9Device() with renderer properties.
0cd4b7d3e3 Added display properties
a02afbaea5 Clean up window properties when the window is destroyed
8668943746 Standardized property names for internal SDL properties
a4c6b38fef Fixed FreeBSD build
d9e6dcc650 Fixed FreeBSD build
7cc3e94eb2 Store the requested muted state
ce9e1bd324 Don't mute the console input if we can't read the keyboard
0a1b6b270f sdlchecks.cmake: Clarified the reason why shared X11 mode doesn't work
80b2bbad21 Removed useless branch test
15bc12165a Actually we need to enumerate the 8BitDo Xbox SKUs
8049af3355 Assume all 8BitDo Xbox controllers have a share button
ed1e0c1530 Make sure joysticks are locked when adding and removing them
415283ef38 Fixed checking for linux/input.h
fea2504a37 Prioritize the pipewire audio driver over ALSA
04e98d2236 Added missing calls to SDL_InvalidParamError("surface")
521bbcc15e Destroy the window surface if we've created it for the software renderer
28e623c504 Added a mapping for the Atari VCS controller connected over Bluetooth (thanks @WizzardSK!)
4106697774 Make sure we include the null terminator in XLookupStringAsUTF8()
b5057edf29 Remove unused SDL_TextureModulate enum
9458cbf75e Removed unused SDL_OSEvent
1a57f6bb29 wayland: Remove QtWayland extensions
5f920d6639 fix emscripten build after commit 07cb7c10
22016b4eae Enable the 5th player LED on the DualSense controller
66cf30c2de Removed misleading comment
dbcd390cdf Log drag and drop position updates in SDL test programs
07cb7c10a1 Fixed connecting and disconnecting real-joysticks closing virtual joysticks in Emscripten (thanks David!)
869257a5c1 SDL_migration.cocci: Added a thing for SDL_Vulkan_CreateSurface.
c6d9fb1ad7 hidapi: Avoid memcpy'ing to NULL.
4d1aecc225 vulkan: Patched to compile on iOS.
fccec65afe Sync SDL3 wiki -> header
2f92807087 vulkan: SDL_Vulkan_CreateSurface now accepts an app-provided allocator.
c53843a961 docs: Remove Doxygen `\brief` tags.
c132295ad7 SDL_FlushEventMemory is not a public procedure.
7ac281f800 Sync wiki -> headers.
f7d40b7594 Added 10-bit pixel formats in addition to SDL_PIXELFORMAT_ARGB2101010
3e4d7e48b0 Fixed memory leak in XInput code
bc3d9e99f3 Only save ibus_addr_file after we've successfully read an address from it.
04dfca958a Added a note to events indicating that memory is owned by SDL
20cd789bab Improved migration documentation for the event memory change.
459f17257c cmake: fix MSVC unrecognized option link warning
761390b62f cmake: detect linker id, and assume MSVC does not support version scripts
d2e005ee13 dynapi: remove duplicated SDL_LoadWAV entries
21ff699251 test: Fix popup test crash on exit
70c149c88f Automatically clean up memory associated with events
1a83bf2399 fix a possible memory leak in SDL_vasprintf()
59b37d0e5b cmake: fix Windows unrecognized option link warning
9302d7732d Fixed touch normalized coordinates
ff3c20a799 Sync SDL3 wiki -> header
17a0fe3a0c Sync SDL3 wiki -> header
14d2471a8f Sync SDL3 wiki -> header
2ad22eeeb5 Sync SDL3 wiki -> header
930438dfb7 Added note that the #ifdef is for !__IOS__
c56583fe45 Fix duplicate symbol on iOS/tvOS
2b62f25a6f Add SDL_sysmain_callbacks.c to the Xcode project
4ab31ca678 Fix dropping file event
5dce4bc716 Makes SDLInputConnection and DummyEdit public classes (thanks Cole!)
d3f2eb2aba Use XINPUT_STATE instead of XINPUT_STATE_EX (thanks Andrew!)
3a482ebae0 Add createSDLMainRunnable() to SDLActivity (thanks Cole!)
b9784feb24 Fixed potential uninitialized memory access (thanks Mathieu!)
75ea3a8d32 Dynamically allocate long text for SDL_EVENT_TEXT_INPUT events
2a1660ab51 Additional cleanup for SDL_RWprintf() (thanks @sezero!)
e5f2cea234 Sync wiki -> headers
ad842dd5ad Fixed a typo in SDL_log.h
f9d11807c0 Added SDL_RWprintf() and SDL_RWvprintf() to do formatted printing to an SDL_rwops stream
52c4e3eab3 events: Update self-referential pointers when copying event objects
91f0456391 Add the source application for drag and drop events (thanks Nathan!)
1a8bf31a69 include: Fixing whitespace on SDL_MixAudioFormat.
d07a264a9b Use the default UCS2/UCS4 conversion rather than non-portable INTERNAL encoding
780b6612a9 wayland: Wayland_Vulkan_GetInstanceExtensions didn't set the count variable.
46b940d571 Updated documentation to note that the event callback is called on the same thread as the main iteration callback
4481754359 Make sure we only dispatch events on the main thread when using application callbacks
274da8561c Updated the migration guide to note that you can check the return value of SDL_AddEventWatch()
ad9dcdbbce Clarify that you should use the other field when reading the event
a19029e3c1 docs: Updated README-main-functions.md based on feedback.
019468dc59 main: Check for SDL_AddEventWatch failure, now that it can report it.
7e445da569 Added SDL_CleanupEvent()
c4bf05fd9d Added subsystem refcount tests to testautomation
7f65ed6461 Handle subsystem dependencies recursively
a6b85c81cc Fixed build
3ab6670cb1 Sync SDL3 wiki -> header
f439ccfc1a Updated return values for SDL event functions
e0379c3b37 Grab events in large chunks in SDL_IterateMainCallbacks()
ad5264e54f Don't run SDL_IterateMainCallbacks() if the init call returns an exit code
dad1a84be4 Fixed building Vivante video driver
fe175d025f Fixed building Vivante video driver
0b460f34ba The HP HyperX controllers have a share button
f3261fedcc Code cleanup now that SDL_bool is equivalent to a C boolean expression
a76d8e39aa Changed SDL_bool from an enum to unsigned int
cf7e5bd0e8 Sync SDL3 wiki -> header
853c28e624 docs: Added first draft of README-main-functions.md
70d75b4a23 Sync wiki -> headers
ea02630143 More audio migration clarification
14980b25a8 Clarify documentation for audio callback migration
6cf84e2c5b cmake: fold HAVE_INPUT_EVENTS into HAVE_LINUX_INPUT_H
5e869d1b35 fix Cocoa_Vulkan_GetInstanceExtensions prototype for Mac
07a776f255 include: Fixed documentation for SDL_Vulkan_GetInstanceExtensions.
5b3a2c6df6 docs: Updated SDL_Vulkan_GetInstanceExtensions info in README-migration.md.
9224a0a2d8 Fix emscripten, android, uikit and windows
d0d8b28df1 Change SDL_Vulkan_GetInstanceExtensions
338974bb29 SDL_test_memory.c: fix build against older windows SDKs.
618d15bce6 Fixed typo
ad0af48883 Check to make sure the Windows joystick device has buttons and axes
ac6b32bb02 gendynapi.py: Discard SDLMAIN_DECLSPEC functions.
9c664b0062 main: Added _optional_ callback entry points.
9323417e9c Fixed gendyapi.py parsing of SDL_RELEASE_GENERIC
759cdf6159 audio: Fixed GetFirstAudioDeviceAdded().
0e614d9179 audio: Massive reworking on thread locking.
40fb76196c audio: Don't let simplified audio streams bind to new devices.
24e3328cca audio: Don't reset device ID counter on subsystem init/quit.
5d95cbde37 cmake: reset check state before testing -fobjc-arc
f18120c83c cmake: check -fobjc-arc compiler flag on Apple platforms
4aacc4b92e cmake: file(RELATIVE_PATH) needs 2 absolute paths
dcc8805c21 testaudio: Fixed compiler warning on Visual Studio.
9cb259e865 audio: Never SDL_PushEvent from anywhere but SDL_UpdateAudio().
875e45e70b wayland: Sanity check pointers and protocols before confining
0e87b71d08 wayland: Check the relative pointer handle before destroying
6127ac0871 Use SDL_DISABLE_ALLOCA instead of HAVE_ALLOCA in SDL_stdinc.h
552bee47cb Clear any previous errors if we successfully show a message box
343da852a6 Don't try to use the Wayland messagebox if we're not in Wayland
f63e9a8a3f wasapi: Handle disconnected devices that get reconnected.
5fa7b291d4 wasapi: Fixed memory leak if new audio devices fail to add.
468c386686 wasapi: Handle disconnect notifications from the management thread, too.
ce3be02b48 wasapi: If device is marked as a zombie, don't try to resuscitate it.
85923049a6 wasapi: Patched to compile.
9bec57309c wasapi: Proxy default device change handling to management thread.
c45b5121ce audio: Fixed potential race condition.
8b6da3c701 Fixed making the EGL context current when resuming on Android
2e9eb1073d Sync SDL3 wiki -> header
e6116d399a mutex: Removed SDL_MUTEX_MAXWAIT.
82f48be3ef Sync SDL3 wiki -> header
899eb0d042 thread: Locking mutexes and rwlocks are now void functions.
082ef41566 alsa: Fix crash from invalid handle pointer
a9aa15c792 CI: change FreeBSD CI runner to cross-platform-actions.
23ceae94c9 Fixed Xbox 360 Controller support using libusb on Linux
ace0c2c297 mutex: Fixed bug where generic SDL_TryLockMutex would incorrectly block.
f52b330ed8 Added support for the HP HyperX Clutch Gladiate controller
b61706373c n3ds: Check that audio thread name starts with "SDLAudioP"
6827b3331d n3ds systhread - use 80kb thread stack size as default, remove hard cap
e4cd1d4059 n3ds systhread - prefer to put audio thread on system core
1023d8ec84 SDL_n3dsaudio.c - don't risk leaving current_priority uninitialized
07171be596 SDL_n3dsaudio.h: use triple buffering
6efe957159 SDL_n3dsaudio.c: separate mixer locks from audio device locks
39a961ba41 Added support for "%[]" sscanf syntax
124a0050b6 Fixed warning: no previous prototype for function 'SDL_UpdateAudio'
b16165a33f rwlock: SDL_UnlockRWLock was incorrectly tagged with SDL_RELEASE_SHARED.
865dd04068 pulseaudio: Don't use a hash for device change detection.
b8cc51875a Fixed build
0413e21e54 Fixed audio device removed events for ALSA
5ba03d377a Revert "Fixed audio device removed events for ALSA"
a774694be0 pulseaudio: Simplified default device change detection code.
e57fef8f0b Fixed audio device removed events for ALSA
4280d4b359 Fixed warning C4210: nonstandard extension used: function given file scope
182cfc3265 pulseaudio: Rework how we manage default devices and hotplug.
b2ae1e835f pulseaudio: Change debug printf calls to use SDL_Log instead.
38afd48daf Added a single source of SDL object IDs
e07f6c0a17 SDL_IsJoystickProductWheel() returns SDL_TRUE for Asetek wheelbases (thanks @IOBYTE!)
c98a14fdeb Renamed display added/removed events for consistency with the rest of the API
c2a3112b6f Added "--substring" to the help for rename_symbols.py
a844d90942 Add missing error reporting in Android_JNI_FileOpen()
4ac38d13dd alsa: Don't touch free'd memory in hotplug thread.
43d41c9dcb audio: Another attempt to make device add/remove work vs event watchers.
9abc692156 audio: Another attempt to deal with device destruction from device thread.
33c9eeec7c Revert "audio: Device threads don't increment physical device refcounts."
e5a15f94e2 Revert "Check to make sure the Windows joystick device has buttons and axes"
70fd8e2ba2 Lock joysticks when adding gamepad mappings
76f81797b7 audio: Device threads don't increment physical device refcounts.
594fda4120 Sync SDL3 wiki -> header
0d7c5a2c56 Updated Android API documentation
0df888c584 Moved Android low latency audio behind a hint "SDL_ANDROID_LOW_LATENCY_AUDIO"
142366c837 Sync SDL3 wiki -> header
3a4c9d6990 Fixed build error when API logging is enabled
1f8f82b379 Removed redundant thread-safety information
a6edc75fe7 Sync SDL3 wiki -> header
3c8edeb79b Clarified SDL property thread-safety information
4fa821cb3e Sync SDL3 wiki -> header
1c70760c0b Added thread-safety information for the new SDL properties API
15533dce05 Cleaned up warnings from check_stdlib_usage.py
bf269571fc jack: Removed FIXME comment that has since been fixed.
797b70877d audio: Remove stub header SDL_audio_c.h.
9d7c57234a audio: Cleaned out most remaining `/* */` comments for `//` style.
0ff67dc21b video: Fix compiler warning about SDL_ReadSurfacePixel not being declared.
81c77396af opensles: Patched to compile.
442e84916a opensles: Fixed capitalization to match other SDL backends.
34914bfb49 alsa: Clean up device handles, now that hotplug thread cleanup is in place.
48d80efb51 Fixed warning C4701: potentially uninitialized local variable 'props' used
f7dc63bcc3 audio: another windows wasapi build fix.
dd98330076 audio: fix windows wasapi build.
7a52f7b3fd audio: Split Deinitialize into two stages.
e55e556f32 alsa: Fixed minor memory leak.
b45a0d9016 Updated the documentation for SDL_LoadWAV_RW()
435e7ce663 Check for device disconnection in HIDAPI_JoystickOpen()
b733adb503 audio: Fix device refcounting vs ProvidesOwnCallbackThread backends.
c6f08c2553 testaudio: Removed debugging code.
d5dac0ad27 testaudio: Deal with a texture being unexpectedly NULL when scaling.
b19e68c8ec testaudio: Properly display playback progress, regardless of data source.
8c39269279 audio: Fix audio stream format when binding to a capture device.
f26b838a3e jack: Check for sample rate and buffer size changes from the server.
063cb0df6b audio: Fixed comment typo. "deref" should be "unref"
354611a0c6 testaudio: Fixed some bugs Valgrind pointed out.
a17f3ba916 audio: Reworked audio device disconnect management.
6ddd96a4d0 Fix some wrong gamepad/controller event enums
8df68b4120 hashtable: Moved over to single-line comments.
1c6d996108 testaudio: if the SDL_Renderer is already gone, don't destroy SDL_Textures.
b22ffb9797 audio: Fix some logic errors in the new device hashtable code.
e526dc64bd Don't set unused variable
6664437748 hashtable: Don't rearrange bucket elements during SDL_FindInHashTable.
8ac5c84ad1 audio: device thread shouldn't touch `thread_alive` after object is free'd.
b17151eb16 testaudio: Don't crash if renderer is NULL (happens during shutdown).
7f408e57ee audio: Keep all available devices in a hashtable instead of linked lists.
0aba2c97db hashtable: SDL_IterateHashTable might as well provide both key and value.
95a9271dbf audio: Never lock a device while holding the device_list_lock.
9aeabb0b05 Fix macOS build error by #8269
382751c4b5 testffmpeg: print usage of options to change audio/video codec
f91bde64d5 testffmpeg: Only enable blending if we're using a texture format that supports it
516d6f9efc testffmpeg: added support for YUVA formats using swscale
ac71831350 Sync wiki -> headers
d18f910248 testffmpeg: added the ability to specify audio and video codecs
72034b9a07 wayland: Fix primary selection handling when the protocol isn't supported
e152129787 Fixes #8190. From #7249, reverted the hunks other than #7239.
b79db0a6ea Fixed potential wraparound issue with property IDs
c9ccf0670c Add unsupported functions to dynapi
25ce87979d Always provide an implementation for all SDL3 symbols
3a36433a3c cmake: test -Wl,--version-script with minimal version script
0efa196989 dynapi: implement SDL_DYNAPI_entry even when building SDL without dynapi support
bf64fecf19 testffmpeg: allow resizing of the video window
efa9a45048 Clarified that testffmpeg will resize the window to the video size
4368f70ff9 Added properties to various SDL objects
973c8b3273 Added SDL properties API
2bca4671a6 audio: Allow audio streams to be created when the subsystem isn't initialized.
1ae33f6751 cmake: optionally install pdb's
0d5cad91b1 We need audio converters initialized in SDL_InitAudio()
1c3a0ade74 audio: Whoops, this stream format change is only for capture devices.
10fab3a39e pulseaudio: Stop the threaded mainloop before destroying the context.
0b71898cb1 Make it clear that the string comparison isn't a boolean check
6c8ad975c7 Like mutexes, operations on NULL rwlocks are no-ops
c552cc6847 We don't require the audio system to be initialized for audio format conversion
9a5f7b17c6 Use SDL wrapped getenv function
044046bc50 audio: Fixed assertions when capture devices have wrong audio formats.
bb2f767f5d testaudio: Make program usable without a 3-button mouse.
321fc18417 README-migration.md: Added note about SDL_HasRDTSC removal.
82f54af617 x11: Properly check for XInput2 support before using it.
b654427537 Added support for the PowerA Nintendo Switch Nano Wired Controller
dc2a5f6ab2 Fixed error C2054: expected '(' to follow 'inline'
a7ae1de9a6 Fixed warning C4028: formal parameter different from declaration
3a47fb7208 The sensor and joystick instance ID generator isn't guarded by a lock.
aee4862958 ci: stop FreeBSD job after 30 minutes
99fa0d6cae Disable low latency audio by default when using AAudio on Android
ebfbd7327b testffmpeg: use SDL_test to parse arguments and track memory
ee53e4d319 cmake: check ffmpeg capability instead of version
2d62c65a75 Fixed build warning
bf72704bfd audio: Disable NEON sample conversion until test failures are fixed
0fe95cfba3 Sync wiki -> header
adcace6f95 Added a "--software" option to testffmpeg
86ada8a9f0 fix testffmpeg.c build.
303f4e965c testffmpeg works with ffmpeg 5.1.3 and newer
2bd478ae65 Added SDL_GetTextureDXGIResource() to get the DXGI resource associated with a render texture.
a842446f62 Added support for 0-copy decode and display using D3D11
d830cd140b Added support for 0-copy decode and display using Apple VideoToolbox
1bf913b29a Added support for 0-copy decode and display using VAAPI and EGL
ce8161e0cf Make sure we're building with ffmpeg 6.0 or newer
ed6381b68d Allow setting any number of sprites over the video
ebf5e08fa1 cmake: use *_STATIC_* variables when linking to a static ffmpeg
88f2fb9dcf Added an example of video decoding with ffmpeg
d88bf687a8 surface: Document the in-memory layout of the pixels
3698630bbc pixels: Document the naming convention
04edb38cdf shape: Use SDL[Test]_ReadSurfacePixel
f5745c3a67 surface: Add a private SDL_ReadSurfacePixel
0d68f45879 test: Extract SDLTest_ReadSurfacePixel
55a1458ed0 audio: Changes to one logical device must update all sibling logical devices.
8e03ea4383 hashtable: Use Create/Destroy naming, in the SDL3 style.
568902b64e hashtable: Added src/SDL_hashtable.[ch].
8745a9949b add-source-to-projects.pl: Fix adding files in the base src dir.
836927edf8 wayland: Try to skip the Wayland driver if not connecting to or in a Wayland session
2a9480c815 wayland: Add null check for zenity version string
f30392da5b Fix assertion in LINUX_JoystickSetSensorsEnabled()
4e59bf6cb9 SDLTest_CompareSurfaces: Output RGBA values of first differing pixel
b2ddfbbec3 SDLTest_CompareSurfaces: If surfaces differ, log their formats
b028fd9604 SDLTest_CompareSurfaces: Log better messages if NULL or different sizes
183606d3d4 testdrawchessboard: clean up renderer and window
4f0642bf47 triangle: don't read destination pixel when you're going to discard it anyways
4cd0c13823 blit_slow: don't read destination pixel when you're going to discard it anyways
49abb9c1fa aaudio: Fixed a comment.
0eb8651d5e Do not report gyro/accelerometer if we can't read axes info
ff57867516 audio: Fixed copy/paste error that was checking wrong variable.
d2d4914ac3 audio: WaitDevice/WaitCaptureDevice now returns a result.
a0820ed833 directsound: Cleaned up WaitDevice.
6c33a05bdb audio: Removed unused AllowsArbitraryDeviceNames variable.
f1fc198278 audio: Destroy the logical audio device before sending DEVICE_REMOVED event.
64ec208479 Fix log message spelling
9111c5e178 tests: Disable mouse warp test under Wayland
de5068f4e4 audio: Commented out a currently-incorrect assert.
3abb464f10 ci: disable Intel compilers
251f8fa272 Revert "Do a better job of finding default ALSA devices"
8857b0f13a Use the device audio format for the lowest latency
806e11ac00 Update sample processing bookkeeping when recovering the AAudio audio device
482c238953 aaudio: Deal with device recovery.
a8813b58a6 aaudio: Change an int to an SDL_bool.
8923305f34 We don't need to wait a full 10 ms, just delay a bit
6a152676bb Wait a bit when snd_pcm_avail() returns 0
b4372de186 alsa: Cleaned up remaining debug logging.
a063c943dc pulseaudio: Use pa_stream_begin_write to avoid an extra buffer copy.
0471a93706 alsa: Simplified PlayDevice and CaptureFromDevice.
776d9d0ee3 alsa: Convert `/* */` comments to `//` comments.
64fee85c69 alsa: More efficient audio thread iteration.
47cba08259 VisualC/pkg-support/cmake: remove subdir from public header include path
61e9a9dd56 pulseaudio: Just feed the device whenever it asks for any amount of data.
4f76f9b0a7 pulseaudio: Use correct buffer size of stream, wait less between fills.
d95d2d7051 SDLTest_CompareSurfaces: Decode pixels correctly on big-endian platforms
d65861f049 Do a better job of finding default ALSA devices
ba65ef5ce7 Recover from -EPIPE in snd_pcm_avail()
5be5000fa1 cmake: make HEADERS_DIR a required argument of SDL_generate_manpages
5c1a91a4e1 ci: make sure perl is installed + build docs with MSVC toolchain
6248472c0c test: Accept small numerical differences in more mathematical tests
a2c5dc6507 pulseaudio: Added typedef needed for compat with ancient Pulse installs.
f24551f6d1 pulseaudio: More workarounds for extremely old Pulse installs.
441a5b707b audio: Adjusted const/static fields on some variables.
280c2c1d7d pulseaudio: Revert "pulseaudio: Require PulseAudio 5.0 or later for SDL3."
4db2b968af audio: simple-copy path should check if device is paused.
505dc4c39c wasapi: Deal with device failures when we aren't holding the device lock.
ea5f59c234 Removed unused code
a6854098f7 Fixed stuttering on Android when using the AAudio driver
a5175e5ed0 audio: Fixed bug when setting up mixing formats.
9667aa18e6 wayland: Check that the data device supports the release method before calling it
4454dc400b wayland: Null all Wayland manager objects after freeing
e1789b320e video: Streamline a little deinit code.
2a1058713c Bump libdecor feature check to look for 0.2.0
f5886f11d0 cmake: let every test depends on pretest
f45761908a Move check for SDL_Delay upper bounds to testtimer
a84389f6bb libm: use union for infinity
474c8d0073 testautomation: don't do float equality tests
a6bc6b882c ci: always upload the artifacts
85e3099ba4 testautomation: only require accelated renderer with non-dummy video driver
26fd231151 ci: run tests on msvc workflow
0e955a9127 cmake: run testautomation with CTest
1375d2049d SDL_iconv_string() defaults to UTF-8
70a1bc6973 Check for NULL before dereferencing newly allocated memory
752f14e5a6 wayland: Convert some memcpy calls to copyp
9284a03053 wayland: Remove some unnecessary helper functions

git-subtree-dir: external/sdl/SDL
git-subtree-split: 0d7df16812c75c4a587d7d2673e3d1a5f2c2879b
2024-01-17 16:26:06 +01:00
7359e30c3e adjust cmake for new toxcore files 2024-01-15 23:43:47 +01:00
61accfe184 Squashed 'external/toxcore/c-toxcore/' changes from 73d9b845a3..e2c01e457b
e2c01e457b refactor: Use enum-specific pack functions for enum values.
afc472402b refactor: Factor out union pack switch from event packer.
6caa7ce4b1 cleanup: Move the 2-element array pack out of individual events.
687af81f20 cleanup: Remove empty test doing nothing.
fcf5882428 test: Add printf log statement to group_moderation_test.
b4d8826228 cleanup: Remove old type-ordered event getters.
8c35e0fefb feat: add ngc events
97bdd83937 refactor: Make event dispatch ordered by receive time.
001d00ab30 fix: dont resolve to ipv6 addresses when its disabled
d3b935f63f fix(test): tests use ipv6 by default, even with USE_IPV6 set to 0
29fc5ea1f7 chore: add clangd files to .gitignore
d30c81acbc refactor: Move file streaming test to its own file.
acdc67387b fix(ci): window builds now build in parallel
REVERT: 73d9b845a3 cleanup: Remove old type-ordered event getters.
REVERT: b0840cc02d feat: add ngc events
REVERT: 7df9a51349 refactor: Make event dispatch ordered by receive time.

git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: e2c01e457bfb8a59537175c8fe17ca9ab1c9e3e1
2024-01-15 23:32:23 +01:00
5effe64474 Merge commit '61accfe1846e1f857fcdf84d27fb3d6e78fb46a3' 2024-01-15 23:32:23 +01:00
b1fe064484 Squashed 'external/toxcore/c-toxcore/' changes from 6d634674a9..73d9b845a3
73d9b845a3 cleanup: Remove old type-ordered event getters.
b0840cc02d feat: add ngc events
7df9a51349 refactor: Make event dispatch ordered by receive time.
bcb6592af5 test: Add C++ classes wrapping system interfaces.
4cea4f9ca4 fix: Make all the fuzzers work again, and add a test for protodump.
c4e209ea1d refactor: Factor out malloc+memcpy into memdup.
87bcc4322d fix: Remove fatal error for non-erroneous case
REVERT: 6d634674a9 cleanup: Remove old type-ordered event getters.
REVERT: d1d48d1dfc feat: add ngc events
REVERT: 994ffecc6b refactor: Make event dispatch ordered by receive time.

git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: 73d9b845a310c3f56d2d6d77ed56b93d84256d6e
2024-01-14 21:51:01 +01:00
2f44996edc Merge commit 'b1fe0644843f5d08cd203bb5d4cdb923172f247b' 2024-01-14 21:51:01 +01:00
18ca88a0d4 asan in flake 2024-01-14 21:48:41 +01:00
565aa4b7eb update deps and adopt 2024-01-14 18:50:23 +01:00
b117da5ccf add compute powersave extreme 2024-01-13 18:38:12 +01:00
8eb4892b49 Squashed 'external/toxcore/c-toxcore/' changes from 8f0d505f9a..6d634674a9
6d634674a9 cleanup: Remove old type-ordered event getters.
d1d48d1dfc feat: add ngc events
994ffecc6b refactor: Make event dispatch ordered by receive time.
812f931d5f fix: Make sure there's enough space for CONSUME1 in fuzzers.
50f1b30fa9 test: Add fuzz tests to the coverage run.
df76f5cf47 chore: Move from gcov to llvm source-based coverage.
072e3beb3f fix: issues with packet broadcast error reporting
6b6718e4d2 cleanup: Make group packet entry creation less error-prone
5b9c420ce1 refactor: packet broadcast functions now return errors
af4cb31028 refactor: Use `operator==` for equality tests of `Node_format`.
9592d590cf refactor(test): Slightly nicer C++ interface to tox Random.
c66e10fb7a refactor: Minor refactoring of get_close_nodes functions.
ebc9643862 fix: don't pass garbage data buffer to packet send functions
32b68cffca cleanup: Some more test cleanups, removing overly smart code.
0426624dcb refactor: Assign malloc return to a local variable first.
afc38f2458 test: Add more unit tests for `add_to_list`.
05ce5c1ab9 test: Add "infer" CI check to github, remove from circle.
REVERT: 8f0d505f9a feat: add ngc events
REVERT: 9b8216e70c refactor: Make event dispatch ordered by receive time.

git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: 6d634674a929edb0ab70689dcbcb195b3547be13
2024-01-12 21:30:48 +01:00
82fe4c3dd7 Merge commit '8eb4892b4976e82e020d0e30dcf8f0705b76bb4e' 2024-01-12 21:30:48 +01:00
78b0e9a77f more small fixes 2024-01-12 19:04:50 +01:00
7fa6aa7ac2 forgot to update plugin 2024-01-12 18:51:29 +01:00
20b4cdc5f1 sync and delivery (and unused read) states and other smaller refactors 2024-01-12 16:45:52 +01:00
7c576dd4d0 make fade less noticable 2024-01-10 14:17:29 +01:00
f6cda522ca correct message order 2024-01-09 22:38:13 +01:00
3d2f5b644b make toxcore work 2024-01-09 20:36:06 +01:00
9ace11a0e2 Squashed 'external/toxcore/c-toxcore/' changes from f1df709b87..8f0d505f9a
8f0d505f9a feat: add ngc events
9b8216e70c refactor: Make event dispatch ordered by receive time.
814c12a6f4 cleanup: Add dynamically derived array sizes to the API.
226b23be12 cleanup: Add explicit array sizes to toxencryptsave.
ef33cb4de0 cleanup: Add Toxav alias for ToxAV.
1da723b34d cleanup: Make Tox_Options a typedef.
b148a2afff chore: Simplify msvc build using vcpkg.
5cac6d7eb1 cleanup: Move `tox_get_system` out of the public API.
c9ca4007e3 refactor: Align group message sending with other send functions.
6c6c0b1b1b cleanup: Make setters take non-const `Tox *`.
a76f758d70 cleanup: Mark arrays in the tox API as `[]` instead of `*`.
baf6d1f6cf cleanup: Make array params in toxav `[]` instead of `*`.
79f55bd06a cleanup: Put the size of fixed arrays into the API types.
1e73698db2 cleanup: Add typedefs for public API int identifiers.
cac074c57f chore: Add fetch-sha256 script to update bootstrap node hash.
32576656bb Make the comment capitalization uniform
aff4dda17c Spellcheck tox-bootstrapd
40b5fbbe9d chore: Remove settings.yml in favour of hs-github-tools.
ebafd51be7 chore: Use GPL license with https.
0e42752f0f cleanup: Move all vptr-to-ptr casts to the beginning of a function.
5407384211 cleanup: Use github actions matrix to simplify CI.
82d8265688 fix: Use QueryPerformanceCounter on windows for monotonic time.
1224e656e3 chore: Add `net_(new|kill)_strerror` to cppcheck's allocators.
6a90ddfe4e cleanup: Run clang-tidy on headers, as well.
bd930cc80a cleanup: Make TCP connection failures a warning instead of error.
fad6e4e173 cleanup: Make all .c files include the headers they need.
ef4897a898 cleanup: Upgrade to clang-tidy-17 and fix some warnings.
REVERT: f1df709b87 feat: add ngc events
REVERT: 1b6c907235 refactor: Make event dispatch ordered by receive time.

git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: 8f0d505f9a598cc41c682178e1589bcc01efe9cb
2024-01-09 16:39:05 +01:00
6104d3b6d1 Merge commit '9ace11a0e2843cbde4af2b6ff7b49bcc6d429f78' 2024-01-09 16:39:05 +01:00
f637c7c942 make fade respect fps 2024-01-09 16:30:27 +01:00
afb886ea7c default to reduced fps 2024-01-09 15:57:20 +01:00
74129cabef fix url open with unsanitized strings 2024-01-09 02:26:50 +01:00
be8ceb861c 1 sec cooldown for reduced fps mode 2024-01-07 22:20:40 +01:00
da0f59a3f5 try fix plugin rendering 2024-01-07 19:37:05 +01:00
000254320e add ubuntu linux to cd 2024-01-07 18:54:18 +01:00
2fac7206d2 pull windows fixes 2024-01-07 18:05:36 +01:00
e8234f2a4a properly seperate tick and render 2024-01-07 16:33:08 +01:00
a0ba0b39d8 add fps reduced mode 2024-01-06 18:23:06 +01:00
845967cf12 more main loop fiddling 2024-01-06 16:45:08 +01:00
92b58cbfa9 faster wakeup 2024-01-06 15:13:45 +01:00
5a0651eaf0 wip low fps mode for hidden states and powersave modes.
dont use yet, as all compute is still done in the render method
2024-01-06 14:38:21 +01:00
14fcaf1d7e why is windows so bad 2024-01-05 16:32:37 +01:00
5a0252d8d0 start screen refactor 2024-01-05 14:47:08 +01:00
9c0ffd38ce last update fixes 2023-12-27 12:49:52 +01:00
b2ae9530a4 Squashed 'external/toxcore/c-toxcore/' changes from e29e185c03..f1df709b87
f1df709b87 feat: add ngc events
1b6c907235 refactor: Make event dispatch ordered by receive time.
b7f9367f6f test: Upgrade cppcheck, fix some warnings.
766e62bc89 chore: Use `pkg_search_module` directly in cmake.
00ff078f91 cleanup: Use target_link_libraries directly in cmake.
c58928cc89 chore: Add `IMPORTED_TARGET` to pkg-config packages.
895a6af122 cleanup: Remove NaCl support.
41dfb1c1c0 fix: unpack enum function names in event impl generator
447666d1a1 chore: Disable targets for cross-compilation.
572924e924 chore: Build a docker image with coverage info in it.
415cb78f5e cleanup: Some portability/warning fixes for Windows builds.
425216d9ec fix: Correct a use-after-free and fix some memory leaks.
4b1cfa3e08 refactor: Change all enum-like `#define` sequences into enums.
d3c2704fa9 chore: Fix make_single_file to support core-only.
0ce46b644e refactor: Change the `TCP_PACKET_*` defines into an enum.
22cd38ad50 adopt event impl generation tool to #2392
f31ea1088a add the event impl generation tool
4e603bb613 refactor: Use `enum-from-int` rule from tokstyle.
19d8f180d6 chore: Update github actions `uses`.
6a895be0c7 test: Make esp32 build actually try to instantiate tox.
65d09c9bfb cleanup: Remove test net support.
REVERT: e29e185c03 feat: add ngc events

git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: f1df709b8792da4c0e946d826b11df77d565064d
2023-12-27 12:37:22 +01:00
ae1fb0fde3 Merge commit 'b2ae9530a405e02a50476c04fc7196c5e9863ad6' 2023-12-27 12:37:22 +01:00
35ebbbef93 prep for new toxcore events 2023-12-27 12:36:38 +01:00
83e200df43 Squashed 'external/toxcore/c-toxcore/' changes from adbd5b32d8..e29e185c03
e29e185c03 feat: add ngc events
2b0dc0f46b add ngc related unpack functions
b2315c50e0 Add groupchat API function that returns an IP address string for a peer
5f863a5492 feat: Add `to_string` functions for all public enums.
0c998a7598 add real timeout test
68c827609a chore: Move s390x build to post-merge.
028b017d79 perf: Slightly reduce bandwidth usage when there are few nodes.
90f7496819 feat: Enable ubsan on bootstrap nodes.
89b6450d66 test: Add check-c run to bazel build.
REVERT: adbd5b32d8 feat: add ngc events

git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: e29e185c03fea7337036e5ef4d1d9080a6cee721
2023-12-24 12:21:34 +01:00
260d3b7818 Merge commit '83e200df43eb790719c40103099c8d70e37c0477' 2023-12-24 12:21:34 +01:00
4ebffd8c63 switch to cerr in test to restore log order 2023-12-22 12:07:43 +01:00
8923e09b36 add asan option and enable for msvc ci/cd 2023-12-22 11:44:59 +01:00
ec4195f18a add linux to test 2023-12-19 01:56:35 +01:00
062ad7ae80 fix mac 2023-12-19 01:47:50 +01:00
aad07611c7 ci/cd blancing 2023-12-19 01:26:56 +01:00
0dcb66f143 add temp test 2023-12-19 00:29:27 +01:00
331c25b0e6 switch to debug 2023-12-19 00:22:23 +01:00
e50844be06 switch to release with debug info 2023-12-19 00:07:47 +01:00
4dd7c98c1a change to 2019 2023-12-18 23:58:15 +01:00
63bad2e99a add deterrent 2023-12-18 22:58:23 +01:00
9ddeea3d06 Squashed 'external/toxcore/c-toxcore/' changes from d4b06edc2a..adbd5b32d8
adbd5b32d8 feat: add ngc events
15ee46d431 add simple test for max sized lossy custom group packet
01e7950c67 increase lossy custom packet size in ngc to the toxcore common max of 1373
9b3c1089f1 Make group saving/loading more forgiving with data errors
55a76003b0 Replace memset(int32_t*, -1, _) with a for-loop
66453439ac fix: also Install header for private/experimental API functions with autotools
3983369103 fix: Enable debug flag for ubsan.
4d1db21102 Update tox-boostrapd hash
e700c31b70 Fix memory leak in group connection
2994441d9c Fix memory leak in save-generator
d0400df13d Fix memory leak in tox-bootstrapd
7a6d50ebe3 Install header for private/experimental API functions
d89677fb5f Remove defunct IRC channel from README.md
26d41fc604 Replace DEFAULT_TCP_RELAY_PORTS_COUNT with a compile-time calculation
63fb2941ca Clarify disabling of static assert checks
65b3375b98 refactor: Use Bin_Pack for packing Node_format.
84ba154f6a group connection queries now return our own connection type
a4df2862ed Replace tabs with spaces
1b6dee7594 Update tox-bootstrapd's base Docker images
a030cdee5c Fix Docker tox-bootstrapd hash update failing when using BuildKit
7cfe35dff2 cleanup: Remove explicit layering_check feature.
d390947245 chore: Upgrade sonar-scan jvm to java 17.
d1e850c56c fix: Add missing `htons` call when adding configured TCP relay.
814090f2b8 chore: Cancel old PR builds on docker and sonar-scan workflows.
83efb17367 perf: Add a KVM FreeBSD build on cirrus ci.
a927183233 test: Add a test for encrypting 100MB of data.
28f39049f6 chore: Retry freebsd tests 2 times.
47e77d1bb0 chore: Use C99 on MSVC instead of C11.
7155f7f60e test: Add an s390x build (on alpine) for CI.
6c35cef63f chore: Add a compcert docker run script.
41e6ea865e cleanup: Use tcc docker image for CI.
e726b197b0 refactor: Store time in Mono_Time in milliseconds.
REVERT: d4b06edc2a feat: add ngc events

git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: adbd5b32d85d9c13800f5ece17c0a9dce99faacd
2023-12-15 15:21:40 +01:00
b95f0498b6 Merge commit '9ddeea3d06045c8ae38cd2d6eed0fc2891c6e146' 2023-12-15 15:21:40 +01:00
7495a50723 add hack to copy the ngc chat id 2023-12-13 14:02:31 +01:00
1cdde5170b update subs 2023-12-12 16:52:52 +01:00
4248d1d9ab disable annoying debug message 2023-11-28 13:19:00 +01:00
4f02c2b55b Squashed 'external/toxcore/c-toxcore/' changes from 75f3c33943..d4b06edc2a
d4b06edc2a feat: add ngc events
cd34b60f0f feat: allow for larger incoming NGC packets
94cf9d1f36 fix: Fix memory leak in the error path of loading savedata.
fc623a5281 tox_new() should return null when savedata loading fails
06d949a701 fix: always respond to version packets with toxcore version
REVERT: 75f3c33943 adopt to #2415 changes
REVERT: 38e4c82fe0 feat: add ngc events

git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: d4b06edc2a35bad51b0f0950d74f61c8c70630ab
2023-11-17 16:01:52 +01:00
05d1648209 Merge commit '4f02c2b55b1eb57f39d69fe7f4319b4cbb50240e' 2023-11-17 16:01:52 +01:00
fd9d14d00c tox private impl + dht caps histo 2023-11-13 16:23:49 +01:00
4e4f62dd20 provide ToxPrivateI 2023-11-13 15:14:30 +01:00
cdc4284cb5 Squashed 'external/toxcore/c-toxcore/' changes from 38e4c82fe0..75f3c33943
75f3c33943 adopt to #2415 changes

git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: 75f3c33943cd3e249cfab3dab122aa4bbb9eec9a
2023-11-13 15:03:22 +01:00
bedbacddde Merge commit 'cdc4284cb50041b5bf7d476561085d629292e456' 2023-11-13 15:03:22 +01:00
32a8dba185 Squashed 'external/toxcore/c-toxcore/' changes from 82460b2124..38e4c82fe0
38e4c82fe0 feat: add ngc events
8099d82397 diagnostic: get the number of close dht nodes with announce/store support
d01c116764 cleanup: make it more clear that assert and uint32_t increment both only exist if NDEBUG is not defined
58fac53429 refactor: Add a `bin_unpack_bin_max` for max-length arrays.
6be29f01e5 chore: Add more logging to loading conferences from savedata.
1195271b7f Fix inversed return values
82276ef5ac cleanup: Fix GCC compatibility.
REVERT: 82460b2124 feat: add ngc events

git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: 38e4c82fe0fc373b9d43ee9ad2b8fe5fd1d26810
2023-11-13 14:02:43 +01:00
d6e5051b15 Merge commit '32a8dba185482fe1a89037bce2a5b0c5e76d4127' 2023-11-13 14:02:43 +01:00
780a67a40d update for toxcore changes 2023-11-13 13:25:41 +01:00
0c7fff3029 update for id comp 2023-11-03 14:41:38 +01:00
e7db39d20a small debuggabilty improvements (scouting for an avatar bug) 2023-10-21 18:07:06 +02:00
869edb8d84 add hacky tox add by id + use ips instead of dns names for bsnodes and relays 2023-10-20 21:40:45 +02:00
dce42b866a hack in extra decimal for progressbars 2023-10-20 02:39:13 +02:00
da19b0ac31 basic settings ui for cats 2023-10-19 23:52:11 +02:00
bc090bdaa8 make main window injectable + start settings window 2023-10-19 17:21:45 +02:00
2a5937652e add file dropping support 2023-10-18 14:24:46 +02:00
b9d4f594ce fix missing include 2023-10-14 16:25:01 +02:00
c79068c561 add tox avatar handling + prio png for paste + other fixes and updates 2023-10-14 15:59:32 +02:00
e7095a1849 forgot to close popup (oops) 2023-10-12 01:10:38 +02:00
3f78e17888 small refactor for pasting img + allow specifying mime type on paste 2023-10-11 21:57:36 +02:00
897253e1d6 toxcore api changes 2023-10-11 03:03:28 +02:00
cd28b26761 fix cmake for toxcore 2023-10-10 20:29:29 +02:00
a3126d581b Squashed 'external/toxcore/c-toxcore/' changes from 67badf694..82460b212
82460b212 feat: add ngc events
24b54722a fix: Ensure we have allocators available for the error paths.
48dbcfebc cleanup: Remove redundant `-DSODIUM_EXPORT` from definitions.
0cef46ee9 cleanup: Fix a few more clang-tidy warnings.
0c5b918e9 cleanup: Fix a few more clang-tidy warnings.
4d3c97f49 cleanup: Enforce stricter identifier naming using clang-tidy.
a549807df refactor: Add `mem` module to allow tests to override allocators.
6133fb153 chore: Add devcontainer setup for codespaces.
620e07ecd chore: Set a timeout for tests started using Conan
c0ec33b16 chore: Migrate Windows CI from Appveyor to Azure DevOps
8ed47f3ef fix incorrect documentation
a1e245841 docs: Fix doxygen config and remove some redundant comments.
b0f633185 chore: Fix the Android CI job
7469a529b fix: Add missing `#include <array>`.
2b1a6b0d2 add missing ngc constants getter declarations and definitions
2e02d5637 chore: Add missing module dependencies.
REVERT: 67badf694 feat: add ngc events

git-subtree-dir: external/toxcore/c-toxcore
git-subtree-split: 82460b2124216af1ac9d63060de310a682a2fd15
2023-10-10 19:37:39 +02:00
1da4a12104 Merge commit 'a3126d581b6a872f0b8d5641f199fb233306e181' 2023-10-10 19:37:39 +02:00
8725bafbdb hide pw in memory 2023-10-10 18:17:27 +02:00
c24dc45e93 hack in compressor types for sip + a bunch of refactoring and fixes 2023-10-09 21:17:00 +02:00
62b00a4bd6 add stb png and jpg encoders, untested 2023-10-06 13:16:45 +02:00
f1f67fe1ba extract webp image encoder 2023-10-06 02:01:31 +02:00
621327bf55 rework the crop buttons 2023-10-05 23:44:18 +02:00
9dabdd32fa add gh sp profile 2023-10-05 19:03:04 +02:00
b479db4989 color anim + fix crop w/h sanitization + hack to determine if png 2023-10-05 18:26:35 +02:00
8633c5eafd crop lr works too 2023-10-05 16:24:51 +02:00
440489f228 sanitize crop 2023-10-05 14:54:42 +02:00
bb824a9fb7 cropping ui partly done (upper left cropper works) 2023-10-05 14:23:39 +02:00
c98dd8a584 try to fix funky timing hanging animation loops 2023-10-04 22:49:33 +02:00
e12d7b1458 add compress to sip (hack webp only) + crop code but not in ui 2023-10-04 20:17:53 +02:00
58edc97787 change cd artifact name 2023-10-04 16:03:29 +02:00
ca6853e2c0 missing include 2023-10-04 02:19:03 +02:00
fc90106d83 openurl file + starting with sending image file and pasting 2023-10-04 02:11:06 +02:00
89bc11eca7 update tox sub with fix 2023-10-02 19:15:03 +02:00
5c4c397f6c update plug and add chat debugging 2023-10-02 17:03:00 +02:00
da774e10e8 update to entt 3.12.2 2023-10-02 15:40:32 +02:00
2f8eca71a4 update subtree entt Merge commit '90ce4bda4e1dc23508bbd6b6923156cd5a370c18' 2023-10-02 15:30:10 +02:00
90ce4bda4e Squashed 'external/entt/entt/' changes from fef921132..344e03ac6
344e03ac6 update single include file to v3.12.2
da56665b0 registry: make ::valid backward compatible
f6f01ef1b snapshot: avoid warnings due to deprecated functions
0ed514628 now working on v3.12.2
a41421d86 update single include file to v3.12.1
c1f6b11f7 snapshot: reintroduce support to storage listeners
b2233064a now working on version v3.12.1
cb974bf56 adjacency_matrix: fix in_edges() is off by 1 in some cases (close #1019)
7b7d82e6f doc: snapshot (close #984)
05c6898fc test: self-fixing archive example for snapshot classes
7ffa459a6 snapshot: drop ::get member template parameter
93e8e94e6 test: basic continuous loader
c4e241662 snapshot: review basic_continuous_loader (and drop shrink)
9c25419b9 test: more on basic_snapshot_loader
1879830df snapshot: drop pointless assert
29298c0eb test: guarantee code coverage, we'll update the test later on
247abef1d test: rollback for code coverage purposes on the snapshot class
6994d98d2 test: typo
9a600ece2 test: snapshot
f91226ef4 snapshot: share ::orphans implementation (to deprecate in future though)
e366ffbd3 doc: snapshot
63b300d39 snapshot: again, dense_map::contains is a thing
afb70d157 test: avoid warnings due to unused variables
49534eec0 snapshot: dense_map::contains is a thing fortunately
3f1277f7b snapshot: use the right allocator for the remote-local mapping
26fad4c38 test: basic snapshot loader
25b3afacf test: basic snapshot
2d25bbb09 snapshot: check registry type
0eb834582 snapshot: small cleanup
124a44052 test: use the new snapshot get functions in the test suite
5c704636e test: use the new snapshot get functions in the test suite
31fd94cc3 snapshot: cleanup to get ready to drop an internal function
573e43272 snapshot: reduce storage lookups
1d8943481 snapshot: drop useless function
e0a1ef7c1 snapshot: check on member type class
48ac0e0eb snapshot: add basic_continuous_loader::get, deprecate ::entities and ::component
bcb6234d9 snapshot: add basic_snapshot_loader::get, deprecate ::entities and ::component
f96796326 snapshot: reject entity type in the range-get (now get instead of get_sparse)
b22c55dd2 doc: typo
4ff5a536c snapshot: add basic_snapshot::get, deprecate ::entities and ::component
fff5f578a test: avoid using deprecated functions in an example
0f44c8c92 doc: reflect recent changes
0b6ad0315 snapshot: * single element only archive functions required * avoid iterating elements more than once
2450b0bc6 test: minor changes (waiting for a rework)
fc8eebf36 snapshot: use component_traits instead of is_empty_v
e4f51f2b7 snapshot: avoid multiple lookups of the same storage
2c2216a89 doc: typo
cafe85180 snapshot: deprecate multi-type component loading function
35e338cc9 snapshot: deprecate multi-type component loading function
8feeaaef7 doc: minor changes
e7a3c4e37 snapshot: add missing [[deprecate(...)]]
ea5c558bd snapshot: cleanup (waiting for further improvements)
94f0ed179 snapshot: deprecate multi-type component loading function
244c35949 snapshot: deprecate multi-type component loading function
1f24fea21 type_traits: formatting
8deaa09b2 test: perform static checks at compile-time
85bffb714 type_traits: std::tuple traits specialization for entt::type_list and entt::value_list (#1011)
325ca310d view: updated ::refresh
d903e268f snapshot: minor changes
f4b26756c snapshot: improved basic_snapshot::component
fb3a34ee9 *: updated TODO
6902bb6c4 doc: typo
379819b2b test: cleanup
59abfbfb5 meta: refine policy check on value types for non-member data
6e2d87184 registry: avoid casting return types directly to better support empty storage
57ec3c85c registry: erase_if (close #977)
4afdf287f doc: minor changes
2810ac7cb registry: suppress a warning on msvc
e0d27f9bf *: updated TODO
de303c999 test: reverse-each for storage entity
1619e780f test: reverse each for plain storage classes
a1e37eca6 storage: reverse-each
c345e7456 doc: note on reverse iterations
d166c026f snapshot: minor changes
5e639996d doc: minor changes
dac2ef5a9 doc: typo
71d7888e8 snapshot: drop redundant check
84a4df9c4 doc: exclude-only views
95bc20319 doc: entity lifecycle
5a9f6d211 doc: cleanup
a29302faa test: more on entity signals
75efa72c6 registry: cleanup ::erase
58a84665b registry: cleanup ::remove
a5263384d doc: drop redundant comments
c0e6759c6 doc: cleanup a little further
d754f7431 doc: cleanup
1df539943 doc: drop pointless tags
c284e6fee doc: minor changes
500239758 test: typo
319ecd808 organizer: fix organizer::vertex::prepare not creating component pools (#1014)
d7891fabc doc: mention named pools support when registering listeners
e287dd041 helper: minor changes
4dee9dde1 registry: named pools support for on_construct/on_update/on_destroy
9bae6e67b doc: update connection helper doc
aa7a7ce25 doc: minor changes
a969468c5 registry: de-deprecate :) on_construct/on_update/on_destroy
a1e76fc63 doc: more about entity storage
d8ed4ca35 registry: refine how entity storage is used internally
3248e3f91 helper: make sigh_helper work with named pools
f00687e6f doc: updated registry documentation
5240c6b60 registry: deprecate on_construct/on_update/on_destroy
67604a88e natvis: update registry snippet
4242dfb8b registry: use entity storage directly as much as possible
f96d8ee83 registry: prepare to split component storage and entity storage
c147ec37c test: try to make gcc happy again
094ddbba3 meta: avoid shadow warnings
634630ca2 test: add missing template keywords (thanks msvc for ignoring them)
d78c26f26 *: updated TODO
fabc6c9bd test: full cross-registry entity-copy example with meta (not strictly required)
b6e8ddd2a meta: fight against the small nuances of the language :)
cf2bbae6e mixin: make it simpler to modify the underlying type
08799616d *: updated TODO
58bebf78d meta: reduce symbols and their sizes if possible
d534fad3e doc: more about views
871dc7a40 doc: drop references to storage placeholders
1fe7c78f7 test: minor changes
22a65f80f test: cleanup
756ea8a38 *: updated TODO
12186cb40 registry: drop internal static storage variables from ::assure
aa9ffb9ee registry: const ::storage<T>(...) returns a pointer to possibly null storage
dcb5aed90 registry: lazily/partially initialize views in the ::view const function
34f6a747a registry: add support for non-existent pools to try_get
912cb2ad5 snapshot: constness review
885488b3d registry: any_of supports non-existing pools now
3d3d3ef2d registry: all_of supports non-existing pools now
a7120b340 registry: coding style
51915205b test: cover stable multi-type model
4a3ee042e view: refine ::storage function
88a1b8d0d view: stable multi-type view ::each(cb) function
7e18a0f96 view: update ::use function
c367082dd view: unchecked_refresh function
9f94b5306 view: double check on none_of
44ed10c50 view: stable multi type view ::find/::back/::front functions
1b2280941 view: stable multi type view ::begin/::end functions
bdabbaa63 view: stable multi type view ::contains function
c79c109b7 view: stable multi type view ::size_hint function
f1a213382 registry: prepare to remove static storage from const assure
17dc06149 view: stable single type view ::each(cb) function
3b8d82330 view: drop unused return
a20829e70 view: ::handle returns a pointer rather than a reference
5be2fdc15 view: stable single type view ::each() function
873b107e6 -: updated TODO
356bbbe53 view: stable single type view ::find function
e3ce4e156 view: stable single type view ::front/::back functions
e02050c51 view: stable single type view ::rbegin/::rend functions
26930633f view: stable single type view ::begin/::end functions
b7a485767 view: stable single type view ::contains function
f54cdccd4 view: stable single type view ::empty function
41c9a32f3 view: stable single type view ::size function
736ef3580 view: make operator bool work properly with partially initialized views
0128cbb4f test: minor changes
ff0a40715 test: prepare test suite for safe invalid views
34f440386 view: avoid using storage, further prepare for empty safe views
b1c78efb6 nativs: updated signal file
28f03ff9c meta: add missing checks on factory<...>::data
a5fe61adb *: minor changes
457f5e59e view: rollback handle() usage and prepare to safe empty views
422fd284e group: refine group ::find function
6f3222573 view: refine single type view ::find function
366bbceb0 doc: use doxygen-awesome-css
7b7f81e08 doc: update reference.md
cfe955f97 doc: update links.md
684ddc9de doc: minor changes
f5d38a9ae doc: drop redundant doxy variable
447e3693f doc: updated doxy file (doxygen 1.9.6)
909490bf6 view: try to make g++ happy again
d90363e4a view: make view pack also work with empty views
ee5de744c view: add missing [[nodiscard]]
d401c88a0 view: assert on null handles
80563b955 view: allow swapping storage elements of a view
c74900057 sigh_mixin: avoid shadow warnings
78867d5c9 group: make msvc happy with constness on virtual functions
d435fc779 basic_entt_traits: suppress a warning by gcc
e6f76e0f9 view: try to make VS happy again :)
1c6b53360 test: minor changes
5c3d8360c view: turn ::use into a self-contained, non-const function
3882c7d9a view: turn ::refresh into a self contained, non-const function
15726218b view: doc
869bfc82c test: minor changes
0eb3d54b2 group: change signature of ::storage to return a (maybe null) pointer rather than a reference
f83290f76 view: change signature of ::storage to return a (maybe null) pointer rather than a reference
686a3b9d7 registry: make storage_for_type available to the final user
4d57d5c32 registry: make ::storage<T> return type explicit
36c21cf7f registry: drop redundant traits usage
7ab10e193 test: minor changes
41467d35a -: updated TODO
d351252a1 doc: entity storage
c6cd4f701 doc: refine storage section
65889cca4 doc: brief mention of void storage
f1914fd94 doc: rearrange a few things
e53af7bef registry: minor changes
b910cd261 *: updated TODO
58d331ca0 registry: minor changes
17f5b0a33 registry: avoid bumping version on destroy if not requested
de386292b registry: deprecate ::each
88bf26a2f registry: deprecate ::assign
3caad4100 mixin: common internal owner_or_assert function
916203a24 test: stress assert on entity limit
62f1971f7 test: minor changes
4fde96357 natvis: updated registry snippet
c3730b65f group: * unified model * drop group handler's size function (no longer required)
1ea072cd3 group: back to the unified model for group handlers
bbe4582ee meta: minor changes
89ab5c328 meta: operator==/!= for meta_func
3a4672793 meta: operator==/!= for meta_prop
0a0446f35 meta: operator==/!= for meta_data (close #1002)
fc58ff74b meta: operator==/!= for meta_handle (see #1002)
fed6831cd locator: support to opaque structures (close #956)
1605c8d9d natvis: updated entity file
d6641c7d8 -: updated TODO file
5079f38e9 storage: allow on_update signals on entity storage
1eab2a4a8 meta: fix constness detection for static functions in meta_type::invoke
c33110765 test: cleanup
117b0bd67 test: more about storage<...>::patch
9b4a6f877 storage: use allocator_traits::destroy rather than destroy_at
f4e6f2b37 group: suppress shadow warning
5971fb7aa -: updated TODO
10dfe7e93 sigh: allow disconnecting listeners during iterations (close #986)
a9208a956 doc: fixed typo
1cc5b32ca test: cleanup
f8a972a3c signal: drop sink::before
5b7cc2002 group: rollback some (no longer required) changes to the owning_group_descriptor
bd34e7f2c group: drop nested groups support, prepare to the large group review and multi storage support
46fe29c3f group: make matching functions virtual for owning groups
c50e2815c group: make owning_group_descriptor depend on the storage base type
fbfee632d group: minor changes
77c59aabf group: group_handler::size function for owning groups
ebb1e8a72 group: single check function for group handlers
1646217f0 group: make types explicit for the next/prev functions
645edfb2b group: decouple constructing and setting prev/next links
61f28298c group/registry: minor changes
d19f97bf2 group: use ::handle() if possible
70c611a84 group: cleanup
286428c19 group: make common_type base of non-owning group handlers
6ec719bcf group: reduce the footprint of non-owning group handlers
11f9bb2d7 registry: use shared_ptr<void> for non-owning groups (prepare to drop the basic handler dependency)
5a1ba5ad7 regisrtry: decouple container types for groups
cf094e7ef registry: finally split owning and non-owning groups as it ought to be
31808bd9a sigh: flip the last commit on its head and drop redundant functions rather than merging them
61a5173a7 sigh: merge a couple of functions
ed6fe9e65 sigh/sink: refine internal definition
e30fa8520 doc: cleanup
ca1069e18 snapshot: avoid allocations if possible
70f73a094 snapshot: drop pointless checks
710fff0e3 entity: make get_t, exclude_t and owned_t constexpr constructible
660bc5843 entity: turn get_t, exclude_t and owned_t into proper classes (close #998)
13295a14e type_traits: v141 toolset workaround for value_list_diff
9ce07ff61 type_traits: value_list_diff[_t]
b272e04ba type_traits: value_list_contains[_v]
28b11912a test: cleanup
b9f096d12 type_traits: value_list_unique[_t]
8c60faa1d type_traits: value_list_index[_v]
1f93ea4ee snapshot: avoid unnecessary lookups
7ca77e53f snapshot: avoid unnecessary lookups
69397f365 snapshot: avoid unnecessary lookups
f907bc066 snapshot: drop redundant checks and avoid unnecessary lookups
bda52701f snapshot: avoid unnecessary lookups
d26f7684c snapshot: minor changes
63d6c2bff snapshot: avoid unnecessary lookups
cc45e7341 snapshot: also avoid using views if not required
5d092bcb1 snapshot: avoid unnecessary lookups
295c68841 snapshot: review ::orphans functions
2664b5255 observer: allocator support
dd3632833 observer: configurable mask type
c8c929e4a group: use type members properly
d1ef7bf15 view: use type members properly
1ab23f17d group: early exit on signal races
a72eb4693 group: minor changes
67579d062 -: updated TODO
766a233f3 view: base_type -> common_type
905671c23 runtime_view: base_type -> common_type
27c1383e4 group: base_type -> common_type
029ccc8f7 registry: base_type -> common_type
cde40d586 group: drop unused using decl
6a16a8a20 group: auto init for owning groups
1a12dede6 group: auto init for non-owning groups
35a78b65e group: cleanup
ada19432f group: support for index based sort
4998e9087 doc: minor changes
471c11c6d sparse_set: respect -> sort_as (naming is hard, you know)
3e13e0b59 group: sort/respect -> sort_as (also decoupled from group types)
53cd105f2 group: reuse pools as much as possible
24b31c379 group: reuse pools as much as possible
def82b534 group: index based get
a424f4ebf view: review get
b8f0a8d8e doc: a couple of interesting articles/series (close #994)
7941226ef group: try to reuse pools when sorting and also please all compilers out there at the same time (aka me figthing ICEs again)
86bbb2f6b group: reuse pools when sorting
3c176f725 test: suppress warnings due to unused variables
3642c8a78 registry: drop [[nodiscard]] from ::group (close #991)
0e80d90a7 group: use storage<idx> as much as possible
4fdf2dccd group: update doc
f8a997e6c group: minor changes
40f676ed1 test: drop unused include
5e346748e test: code coverage for groups and registry
3ef61fe01 meta: support meta member functions on primitive types
3885d280d test: cleanup
f41b91419 meta: allow updating values on meta properties
e0684f634 registry: cleanup/minor changes
fb980a78c registry: further refine the group function(s)
c2430ab48 doc: minor changes
d36d9cb39 registry: further cleanup group functions
0017c08bb group: get pools from handlers
e737ff747 group: get filter from handlers
945dc4093 group: split group handler functions
7ef008511 registry: drop group_data
d2fa68813 registry/group: prepare to get rid of group_data
f22a09a9a group: in-between change to simplify dropping group_data
b0aba79a5 snapshot: minor changes
7c23e4a2f registry: minor changes
7fe035ce4 group: move group size from registry group_data to basic_group_handler
3e7160eda group: minor changes
aaeb686ec group: common base class for group handlers
3fdf4884d group: prepare for group handler common base class
1b23ff4b9 registry: use common group handler types as keys for the group set
88dac318e group: wrap the len of owning groups to avoid changing it by mistake
520c2e660 group: make group handlers work with multiple storage of the same type
f5d0d451b group: split pools and filter in the group handlers
8af6fc0cc group: use ::handle internally if possible
c04b97a31 group: add ::handle function to all group types
1d85414dc doc: drop refs to registry::version (close #992)
c6533827f group: fight with clang format from time to time :)
b5803451b group: make owning groups work with their handlers
3417d66b2 group: make non-owning groups work with their handlers
1e61204e8 registry: deduce group handler type from group type
19c4857ef group: cleanup
66ea94898 registry/group: move group handler to group file as it ought to be
ced6d21c3 registry: break dependency between registry and group handlers
429c7c45c registry: further cleanup things
c03b1111a registry: small cleanup
ebd7d3acd registry: storage based model with pools for groups
5aeec60cf registry: prepare to switch to storage based group handlers
620b4f751 registry: pass handlers to group callbacks
6d58004c1 registry: minor changes to simplify the implementation slightly
df6d926de registry: prepare for a storage based group handler
e63af24cb registry: turn the non-owning group handler in a storage
068d9f8ae registry: discard unused arguments from listeners if possible
c19c848c4 test: suppress warnings due to unused variables
0bf0a0a8f doc: delegate
743e8678e delegate: also support functions that skip first elements (on second attempt only)
a7ad1c06f delegate: prepare to support filtering on both sides
b1af70e70 registry: avoid checking pools in the group handler if possible
c87c3533e registry: avoid checking pools in the group handler if possible
4839a0ee6 registry: cleanup
a0f0c44e6 registry: minor changes
74691dc1d group: just use meaningful names :)
e4957badb registry: split group handler to further refine group management
46791c4c3 registry: turn group handler functions into static ones
56c391784 registry: prepare to rework groups
1fb13d3e9 doc: minor changes
535beb4e2 storage: drop unnecessary use of integral_constant
2d318b88c -: updated TODO
b7f0b76ce entity/mixin: add missing include
d30312f51 entity/helper: add missing include, drop unnecessary traits calls
30772848e meta: avoid unnecessary calls to std::move
eca01a397 doc: add vcpkg badge and vcpkg.link (#985)
35ef0b7ac core: reduces the number of instantiations a bit
19ccba3a6 meta: reduces the number of instantiations a bit
207b7674a doc: fix typo
631c55ba9 storage: minor changes/tests
e7b30fd36 storage: return iterator to elements rather than entities and only if it makes sense
3e959007b storage: ::insert returns an iterator to the range of inserted entities
07ec4ca23 -: updated TODO
6e4946b68 storage: uniform interface to simplify mixin implementation
47ea16f17 test: signals on entity creation/destruction
722857fc0 test: get rid of pointless template parameters
2125b3838 test: minor changes
289de7d57 test: exclude only views
25ecd8e79 test: minor changes
319dfdb07 test: filtered registry view
9dbbcac01 -: updated TODO
f545c8e05 registry: deprecate ::release
c68fa6a65 registry: make ::destroy work without ::release (the latter to be deprecated)
d288ecd70 registry: make ::release use ::bump return value
312d3aba8 sparse_set: bump returns the version in use (for convenience)
4d2b2c6de registry: use traits_type::next if possible
80d55a226 test: increase code coverage
d86a53935 test: suppress warnings due to unused variables
0f7098d0e -: updated TODO
8c96be1e9 registry: deprecate a bunch of functions because of the entity storage
37f396bfe registry: make entity storage storage as any other
75894dc40 storage: update traits_type for entity storage
cdee000ce any: rollback a change that turns vs toolset v141 crazy
54ca62600 dispatcher: refine aggregate check
6f4280ed5 any: refine aggregate check
ddf56b78c storage: backward compatibility on component requirements
53a854f54 any: just cleanup the code to make it easier to work with
4896acac7 storage: typo
e3defeba2 test: suppress warnings due to unused variables
62079908c storage: use proper value type for entity storage
e65a8f2e5 doc: add link to koala engine :)
9f27fb1e5 registry: further prepare to turn the entity storage into a plain pool
04d734e76 registry: prepare to turn the entity pool in a plain storage
df50fa1b5 natvis: cleanup
051872b8c natvis: update registry definition
57ab9e7be registry: avoid using assure if not required
69d95ba75 test: more bench to stress a little an upcoming feature
9caf66d7c test: cleanup
74cb0d40c test: internal rework
deac7f34b dispatcher: refine aggregate support
a9883f27c storage: refine transparent aggregate support
85b1e57d8 sparse_set: drop fast_compact, expect full clear
b7d8e0186 storage: make the entity storage perform a full clear rather than a fake one (still viable via erase)
390a56176 -: updated TODO file
a1b888cce natvis: add optiona storage length item for entity storage
2107dd689 natvis: fix already existing errors due to renaming or design changes
1fca56afe storage: make it easier to refine the natvis file
c0762a6a5 storage: add get/get_as_tuple to entity storage to make it suitable for use with views
f48de1bac test: stress get/get_as_tuple for empty types
c7dfce89e sigh_mixin: refine pop_all
822fafcd4 view: uniform implementation to simplify upcoming changes
1476d4ea9 sparse_set: refine ::respect
c1c63777e -: updated TODO
2fab25ae8 registry: refine internal check
75d449152 -: updated TODO
c7866fb21 storage: use entt traits next function if possible
87987bacd entity: added basic_entt_traits::next (with tests)
bde0219fe snapshot: review basic_continuous_loader::entities
ad64c849b storage: suppress warnings
b808bb83b test: suppress warnings
d0090d35f snapshot: try to make sizes an opaque value to the caller
7a1a06a24 sigh_mixin: avoid shadow warnings
000b17881 -: updated TODO
068b6ed49 registry: first (almost) backward compatible version with opaque hidden entity storage
0187fb48a test: sigh mixin for entity storage types
35a2b3844 sigh_mixin: also support entity storage types
4747c9a4c registry: extended checks to support swap-only entity storage types
7be8d8327 registry: make a couple of conditions opaque
a5d6757d6 registry: prepare to get rid of the vector of entities
3f09d47c8 storage: remove redundant typename keyword
9c06d6ba0 registry: use type member names
b7c819bf4 test: entity storage
9f31803ba storage: swap-only entity storage
1e7deff9c test: drop redundant checks
04ac15d8d test: minor changes
376218991 sigh_mixin: make pop_all use narrow view iterators if any
18d6e466d -: [[nodiscard]]  as appropriate
095ecf314 group: extended_group_iterator::base to return the underlying iterator
433ed863e view: extended_view_iterator::base to return the underlying iterator
0dba68e75 storage: coding style/minor changes
1ab281582 storage: extended_storage_iterator::base to return the underlying iterator
2af5a725e doc: * updated copyright * udpated TODO list
a86bf1332 test: try to make lcov happy
831054bff test: share as much as possible
f94de1c06 test: rework lib stuff to share common files
a3d9503a1 test: try to make lcov happy
3f2b15f9f test: try to make lcov happy
e48817d51 test: try to make lcov happy
d11cebe30 view: uniform design to also help natvis without having to poke into stl internals
77a5efb32 natvis: updated to_entity intrinsic
851006efe -: updated TODO
6fc6b2fb3 sigh_mixin: further improve ::pop_all
ed17a2c48 sparse_set: ::contiguous function
bd00e797a sparse_set: further refine pop_all to make it even faster
e645c4928 -: updated TODO
a425878e8 sparse_set/storage: clear is backward compatible now
f3cd9d374 storage: fixed clear_all counter
b3e93b084 registry: naming convention
314c189c4 test: minor changes
2bb2c5566 build: try to make lcov happy again
d13c126e9 view: avoid name clashes
9b54ee37a flow: propagate allocator to generated graph + internal rework
e1ead9d3e build: update coverage workflow
cf61068dc mixin: suppress a warning with gcc11
82863f829 test: code coverage for range functionalities
e4de59827 test: try to make lcov happy
ccea4c920 memory: code coverage
89166f0e4 build: refine analyzer workflow
7a05a16c5 registry: slightly better destroy (yet not quite there though)
d0854646c test: yet another test to stress the upcoming changes
1e9c9fe5f registry: better, faster range-remove + refine range-erase
80fac8d8e test: minor changes
c774b9838 -: updated TODO
3fd0403cc registry: faster, better range-erase
6eb3347a3 test: a couple of extra functions to stress the upcoming changes
89bceaff7 -: updated TODO
dc25c9c1a sparse_set: invoke release_sparse_pages before clearing the sparse array
e68ba5870 sigh_mixin: add a missing include
c68cb3375 entity: make deletion_policy publicly available via fwd.hpp
59f807fd0 sparse_set: suppress warnings due to unused expressions
232ffebc1 sparse_set: internal clear_all function
3cea845a0 sparse_set: sparse_set_iterator::data function
295f3b32e registry: a couple of extra move calls here and there
254da2c3c sparse_set: better, faster range remove
ecd3b8d93 sparse_set: prevent rework errors as much as possible
c673b9b17 sigh_mixin: slightly improved pop + review insert
cd28de0d6 test: clear-stable bench
672f6a711 test: minor changes
3b50672b7 storage: restore storage_for/storage_type duality, it turned out to be very useful in practice
f0613b1c6 sparse_set/storage: minor changes to reuse type members
2197e160e -: drop file pushed by mistake :)
2dccd9016 handle: discard entity on destruction
2f873f2dd -: storage_mixin.hpp -> mixin.hpp (non-storage mixins are also a thing)
fde1a524e sparse_set: ::get -> ::value (to avoid hiding from derived classes)
055801047 doc: drop references to docsforge + minor changes
79a054a52 sigh_mixin: scope base_type properly
d94e443a1 doc: drop outdated section
3862184e8 sigh_mixin: support self managed storage classes
f40fa3c2f test: * use range destroy * avoid compiler optimizations
01bc93459 test (bench): the new entity storage enables the fast path in all cases
151bd0739 sparse_set: revert optmized range push, it prevents self-managed storage classes
935393aae sparse_set: better, faster range push
fbfde4347 snapshot: avoid unused variable warnings
2ffbe115b component_traits: revert entity customization support
645973eb7 sparse_set: insert -> push
133230797 sparse_set: emplace -> push
b700f5eb5 doc: typo
e60dbdc52 sparse_set/storage: * rename swap_at in swap_or_move to capture the real purpose * define swap_at as a protected function to allow swapping from above
c66623b33 sigh_mixin: avoid hiding basic_iterator type meber
62246d879 storage: avoid hiding basic_iterator type meber
b35f13130 sparse_set: support swap-only mixins
3dd82633a -: drop storage_mixin.cpp, I forgot to do it a couple of commits ago :)
00231bf8a storage: make swap_at non-final to support checks on derived classes
58d392e81 -: minor changes
1d4d99d09 mixin: sigh_storage_mixin -> sigh_mixin
fe3edf2c8 -: minor changes
0864ba042 -: drop useless typename
3a9698001 build: minor changes
423f7a555 is_equality_comparable: detect C-style arrays directly
5db8ad53a build: update gh workflow
c2ab35780 view: make also VS toolset v141 happy
4fb558f14 view: further reduce instantiations
5762a8a08 view: reuse internal functions if possible
ed4c67521 sparse_set/storage: drop move_element
f15789846 config: ENTT_FAIL(msg) -> ENTT_ASSERT(false, msg)
6d20709e0 storage: minor changes
a9a9853c0 sigh_storage_mixin: use entity_type from Type
af14aa4c9 doc: more about signals (sigh_storage_mixin)
24d6b9881 test: minor changes
899f4baa6 storage: * drop storage_for]_t] * make storage_type[_t] deal with constness
c1ab7ba02 sigh_storage_mixin: make all virtual member functions final
9d38f6020 registry: thanks MSVC for accepting invalid C++ code
0efa25cf6 sigh: cool, I keep doing the same error again and again apparently :)
6316b6045 registry: make it work with storage<void> also in C++17
f268fb60a entity: avoid breaking changes due to type members renaming
3520d6915 entity: add base_type
4da7a8451 entity: make checks work with 64b identifiers :)
382dfc3bb entity: strict check on entity/version masks
b6dcdc816 entity: * also expose entity_mask and version mask to the final user * avoid default args with entt_traits::construct for backward compatibility
c9d544089 doc: review/cleanup entity.md a bit (done)
3eb5faeed doc: review/cleanup entity.md a bit (work in progress)
7a328c7ed doc: updated links
6567aa195 doc: a note about listeners disconnection (close #958)
92319f011 entt_traits: split basic impl, simplify def
782d86b6e entt_traits: value_type -> type (cuz it's not a value type after all)
c2cae37c1 entity_traits: make page_size type explicit
1026d26ec entt_traits: drop reserved value
7156803db test: local non-static constexpr variables
f54ed5424 helper: local non-static constexpr variables
f30b50195 algorithm: local non-static constexpr variables
c90ab9aff sparse_set: * break dependency on traits_type::reserved * use a tombstone if all I need is a tombstone
c2f6ca43f doc: graph (close #957)
3e5e41d88 test: cover some corner cases of the flow class
9eafc0431 flow: minor changes
0a82b777b component_traits: support specializations based on entity type
32bcc01a4 component: * make component_traits treat void properly * drop ignore_as_empty_v
9c3fe3546 nativs: entity module
83f8aed58 helper: use traits_type from storage class directly
2fd660274 snapshot: use public registry traits_type member type
a554d406e registry: * public traits_type member type * break dependency on component_traits * use public storage traits_type member type
5f12f872e test: minor changes
be4eb68a3 helper: * break dependency on component_traits * use public storage traits_type member type
df5284d9e view: * break dependency on component_traits * use public storage traits_type member type
0e27d33e7 storage: public traits_type member type
fe6e6ae73 sparse_set: public traits_type member type
9d29713ea entity: naming convention
270d0277d group: cleanup
0bd06c8d5 hashed_string: naming convention
733f215cc storage: break dependency between component_traits and storage_iterator
ad01a69fe *: renaming/coding style
dd9c1dade sparse_set: no need to differentiate template args for sparse_set_iterator
b8f70519f doc: fixed typo
9b9d212dd *: coding style
3fe15969d doc: cleanup
ec4bf222c meta: avoid the +1u trick for 0-sized arrays
1173908ee meta: avoid rebinding when forwarding requests
2595b8a92 doc: sigh_helper
f4e2a8c76 sigh_builder: add all missing .template that msvc kindly accepted anyway
66e1a0565 entity: sigh_helper utility with tests (close #928)
87283dc41 storage: simplified impl in order to introduce multi-type storage more easily
a802ebffe storage: * move storage_type[_t] and storage_for[_t] to fwd.hpp * no need to include storage.hpp when forward defining views
b84b09421 doc: add Arch ECS to references.md (#954)
940fd0939 todo: add a note for a (soon to be released) change
920338be5 doc: add ecsact to links.md (thanks @zaucy for pointing this out)
bcd1155b7 gh: add more gcc and clang versions
1dc88109e gh: update workflows
262c1f53c cmake: only enable -Wdocumentation for clang-cl
4af0a3a0d doc: cleanup
be1641828 doc: cleanup
b54a52fbf doc: fixed typo
ae8815995 doc: fixed typo
62c764f68 doc: fixed typo
2c48cc10a cmake: enable documentation diagnostic for clang
82f286678 sigh: drop redundant function
d56e5a269 registry: propagate allocator to context
1517b2951 doc: document delegate raw access
bea7b43a1 delegate: target member function
2f878f8b5 sigh: refine ::collect
fc68c1b29 view/group: cleanup
9081c185d meta: minor changes
7c4493f23 group: make filter storage available
da4e73ab8 view: make filter storage available
f3e7f98b4 registry: extra check when moving a registry
3925fc612 emitter: extra allocator check when moving
c639130c1 dispatcher: extra allocator check when moving
75c311600 registry: cleanup
e9e14eb49 meta: [[nodiscard]]
d1558304f any: [[nodiscard]]
0531b530b snapshot: minor changes
f9d0178dd workflow: bump iwyu version
b66b8d37e test: suppress warning
05ef4c29d storage: minor changes
9c3d75669 test: cleanup include directives
93651e46f registry: drop [[deprecated]] functions
ea901cbfa test: code coverage
d5dc4f43e doc: meta.md
498e02f15 doc: core.md
d0ea8f4f9 cmake: suppress some warnings for clang-cl, it goes a little wrong otherwise
dec3b7bb3 test: suppress warnings
10bc8b05a test: use /W1 with VS (but for toolset v141, too bugged for that)
ad77b54dc cmake: bump version to get some cool feature/update
b6724b028 group: pass filter storage to groups (in-between change for full storage access)
54270b103 group: make them easily copyable/movable
31dc732a7 doc: graph.md
f0e02d6d3 doc: container.md
156d6e4ea doc: poly.md
4375c1c3d doc: lib.md
24a9cd67e scheduler: forgot to add the fwd file to the previous commit :)
ba8d522c1 doc: add the worst engine (love the name) to the list of links
3ae46214a doc: review process.md
5119fe8d7 scheduler: basic type model with default for common cases
ed0319cdd view: avoid shadow warnings
bc50da6a7 storage: suppress warnings with non copyable nor default constructible types
52b3b4c24 group: suppress warnings for unused variables in case of empty types
74bab529d test: minor changes
b1b143917 meta: [[maybe_unused]] variable to avoid warnings with corner cases
7beb4c85c test: suppress a few warnings (entity)
f3beb5670 test: suppress a few warnings (container)
446c67b69 test: suppress a few warnings (resource)
c4507bd17 test: suppress a few warnings (poly)
61e872bb4 test: suppress a few warnings (meta)
9f22a3e23 test: suppress a few warnings (memory)
653dd5cd4 test: suppress a few warnings (tuple)
bc53ed3be test: suppress a few warnings (flow)
f935bbcce dense_set: suppress warnings due to possible narrowing conversions
c7d505353 dense_map: suppress warnings due to possible narrowing conversions
ea78f1d97 now working on version 3.12
REVERT: fef921132 update single include file
REVERT: e52a93f8a ready to cut v3.11.1
REVERT: cd541f335 storage: * move storage_type[_t] and storage_for[_t] to fwd.hpp * no need to include storage.hpp when forward defining views
REVERT: 255b8be8c view: avoid shadow warnings
REVERT: 8cd7f064a storage: suppress warnings with non copyable nor default constructible types
REVERT: 58ae4117c group: suppress warnings for unused variables in case of empty types
REVERT: cfa1e805b meta: [[maybe_unused]] variable to avoid warnings with corner cases
REVERT: ccedacec8 dense_set: suppress warnings due to possible narrowing conversions
REVERT: 17578dc8c dense_map: suppress warnings due to possible narrowing conversions

git-subtree-dir: external/entt/entt
git-subtree-split: 344e03ac64a1f78424ab1150e2d4778e8df8431d
2023-10-02 15:30:10 +02:00
4afe39dacc unread message status + fade in ui 2023-09-29 18:15:18 +02:00
dd316d2589 update sdl Merge commit '644725478f4de0f074a6834e8423ac36dce3974f' 2023-09-23 18:53:38 +02:00
644725478f Squashed 'external/sdl/SDL/' changes from ec0042081..399bc709b
399bc709b build-scripts.pl: Added add-source-to-projects.pl
ac6827187 Visual-WinRT: dos2unix the project files to match other Visual Studio targets.
34719cba9 Fixed crash in hid_init() if the HIDDeviceManager isn't available
2e92e94eb Make sure we update device->sample_frames in SDL_AudioDeviceFormatChangedAlreadyLocked()
9964e5c5b wayland: Don't retrieve the drag offer strings with every pointer motion event
bac7eeaaa Added missing include
a541e2ac1 audio: Change a few SDL_memcpy calls to SDL_copyp.
54125c140 audio: Only update bound audiostreams' formats when necessary.
e0b0f9a36 testaudio: Fix mouseover testing.
2f3deec24 wayland: Don't process drag & drop events from surfaces not owned by SDL
42bdced05 events: Log file drop position events and print the pointer coordinates
c10d93d3a wayland: Replace magic constant with define
500852153 emscripten: Restore compatibility with existing emsdk releases.
953b55dd6 Use EM_ASM_PTR when the return value is a pointer
a4541a255 audio: SDL_GetAudioStreamQueued now returns bytes, not frames.
703aefbce Sync SDL3 wiki -> header
99421b64d linux: Add portal drag and drop
952c5059b Remove stray Â
eebd5d18a linux: Handle upower's UP_DEVICE_STATE_PENDING_CHARGE, PENDING_DISCHARGE
f8fdb20d8 audio: Destroy all existing SDL_AudioStreams on shutdown.
62d445997 audio: Removed declarations of functions that don't exist anymore.
b656720bc loopwave: Use SDL_GetAudioStreamQueued() for more accurate results.
34b931f7e audio: Added SDL_GetAudioStreamQueued
23206b9e3 audio: Added SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED
c7e6d7a1f audio: Changed debug logging output.
87ec6acf2 audio: Added a FIXME
ac88ffb7e audio: don't allocate buffer in SDL_SetAudioPostmixCallback for NULL callback.
2a950f6ae audio: Replace some SDL_memcpy calls with SDL_copyp.
0dc0434a3 audio: Fixed race condition in subsystem shutdown.
23f60203a audio: precalculate if we can use simple copies instead of the full mixer.
36b0f1141 audio: Optimize setting device formats during audio thread iteration.
4c3e84897 testspriteminimal: make standalone by embedding icon.bmp
2a01f9dcb tests: plug leaks when running with --trackmem
f42bbeca2 SDL_test: track stack frames of allocations on Windows
12c0be028 SDL_test: clear text cache on exit event
b4bfb1831 SDL_test: free state before logging allocations
248b1edd3 SDL_test: destroy windows in SDL_CommonQuit
98da2dd30 SDL_test: don't warn about expected allocations when running with --trackmem
6a381567b Support audio rate conversion up to 384KHz
b2b548a1f Don't hang if IAudioRenderClient_GetBuffer() fails indefinitely
a3a5e1728 Fixed build warning '=': conversion from 'Uint32' to 'Uint16', possible loss of data
6d3e21c27 Fixed android build warnings
fca2f5318 Fixed warning: this function declaration is not a prototype
a72dfa6a5 Fixed sensor timestamp units for third-party PS5 controllers
f6756047a Fixed error: array subscript 2 is above array bounds of ‘const Uint8[2]’
7059a55cc Fixed sensor timestamp calculation for third-party PS5 controllers
c0443e5d1 Fixed crash in SDL_IMMDevice_FindByDevID()
fde8499f6 Use around 20ms for the audio buffer size
e5739d7d1 video: Remove SDL_GetFocusWindow()
39c2f9737 Fix NULL dereference in SDL_OpenAudio
9a23d0e3f Added new audio files to the Xcode project
a62e62f97 Refactored SDL_audiocvt.c
31229fd47 include: Added a note about SDL's iOS app delegate functions.
65aaf3a9a x11: Always update clipboard owner
f622f21e6 Fixed build
5774c9638 Prefer hidraw over libusb when libusb whitelisting is not enabled
9301f7ace hidapi/libusb: only enumerate each interface once
859dc14ad Replaced SDL_GetGamepadBindForAxis() and SDL_GetGamepadBindForButton() with SDL_GetGamepadBindings()
9e50048ab Revert "Removed SDL_GamepadBinding from the API"
9f17d1a9d Don't reference the same function in "see also"
86505ea63 fix SDL_AudioStreamCallback documentation
d885d5c31 Sync SDL3 wiki -> header
2f43f7bc5 audio: Allow querying of device buffer size.
cf9572113 audio: Added a hint to let apps force device buffer size.
47d8c77c6 audio: Choose better default sample frame counts.
8b26e95f9 audio: Change SDL_AudioStreamCallback
9da34e8fb docs: Updated README-emscripten.md.
fd1c54a00 detect fanatec steering wheels
cb4414608 docs: Whoops, this got added by the wiki bridge by accident!
cd633b9a8 Renamed SDL_IsAudioDevicePaused() to SDL_AudioDevicePaused()
c6cad07fa Sync SDL3 wiki -> header
a6e52f9e4 Sync SDL3 wiki -> header
2de2e9d03 Fix flickering of window when using desktop-fullscreen and borderless window on multiple monitors on Linux.  Closes #8186.
723835d16 Windows: fix for client rect resizing larger each time we came from exclusive fullscreen -> windowed on a monitor with HiDPI set.  The problem was we were using the monitor DPI rather than the window DPI so AdjustWindowRectExForDpi was giving us an incorrect size which would be too large for the client rect.  Closes #8237.
ce27363df wikiheaders: Sort undocumented functions.
e22282b09 Added README about transparent windows in Win32
1d1c6e630 Turn off COREAUDIO debug logging by default
52efefca0 wayland: Fix drag offer leak
3a992af44 audio: Added a postmix callback to logical devices.
7207bdce5 render: Enable clipping for zero-sized rectangles
22d81fb3e cmake: use MSVC_RUNTIME_LIBRARY to force MT
a2e17852d cmake: make sure SDL_GetPrefPath is run before testfilesystem
2fb266e0a ci: run tests in parallel
ad1313e75 testaudio: Patched to compile.
5747ddc01 testaudio: Clean up some messy memory management.
fafbea1ce audio: Move internal float32 mixing to a simplified function
116b0ec97 include: minor tweak to audio API documentation
fb1377035 include: Replaced old Bugzilla URL.
38c8fc05c audio: Remove ChooseMixStrategy.
b00cbd76a wikiheaders.pl: create Unsupported.md file with list of functions undocumented in either the headers or the wiki
37e1fc3b5 wayland: Ensure that the toplevel window is recreated when switching decoration modes
f2ca9a615 Added SDL_AUDIO_FRAMESIZE
53122593f Added SDL_AUDIO_BYTESIZE
544351c98 Sync SDL3 wiki -> header
2e7d2b94e Clarify that SDL_BlitSurface() ignores the width and height in dstrect
a2c1984d3 Detect Simagic wheel bases as wheels (#8198)
1d8dfbb22 avoid type redefinition errors after PR/8181
266b91d2f Detect Logitech G923 Playstation as wheel G923 have two different versions - Xbox version is already present in the wheel list, but not the PS version.
cde67ea49 Detect Logitech PRO Racing Wheel for Xbox (PC mode) as wheel Logitech PRO Racing Wheel have two different versions - for Playstation and Xbox. Vendor + Product ID for Playstation version already present in SDL sources, but not an Xbox version
3a932141e Restore audio format binary compatibility with SDL 2.0
e85206ffd wikiheaders.pl: add --rev= option to pass revision string
233789b0d Audio types have the same naming convention as other SDL endian types, e.g. [S|U][BITS][LE|BE]
36b5f3e35 Sync SDL3 wiki -> header
0e552761b Renamed AudioStreamSpeed to AudioStreamFrequencyRatio
47bcb078f Fixed some incorrect SDL_AUDIO_F32 uses
2833f2e7b Fixed OOB access in audio_convertAccuracy test
8387fae69 Sync SDL3 wiki -> header
832181345 docs: Add note about Wayland application icons
825d34475 Make sure that the same timestamp is used for all PS5 events from the same packet
9c1430324 Removed SDL_dataqueue
28b28bd8f Added audio_formatChange test
a59152688 Try and avoid overflow when handling very large audio streams
5394a805f Improved testaudiostreamdynamicresample
e55844274 Added SDL_(Get|Set)AudioStreamSpeed
43c3c5736 Track the formats of data in an SDL_AudioStream
337fed3df Tweaked ResampleFrame_SSE Use _mm_unpack(lo|hi)_ps instead of _mm_shuffle_ps
fd7cd91dc audio: Mix multiple streams in float32 to prevent clipping.
9097573e3 audio: Choose a mixing strategy on each iteration.
bbe2e012a Don't provide the SDL3 header path
c17a35f09 Fixed typo
4f72255eb Fixed README.md link
e0ab59754 Simplified SDL_main.h migration notes
d44bde61e Added SDL migration information to the top level README.md
6ff31e10c metal: Add hint to select low power device instead of the default one (#8182)
8a8aed477 Make sure that we process touch events that position the mouse
f84c87f20 Sync SDL3 wiki -> header
a7eea9997 macOS: Don't raise the parent top-level window when raising a child window, only raise the child window to the top of the parent
a5e721479 Add SDL_WINDOW_NOT_FOCUSABLE flag to set that the window should not be able to gain key focus
b385dc3b6 n3dsaudio: Patched to compile.
4e0c7c91f audio: PlayDevice() should return an error code.
a94d724f1 wayland: add SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL
da5d93d3d wayland: don't define SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_* macro's
f002f7d12 ci: build emscripten with Debug buid type
3699b12ed audio: Fixed some "is_*" variables to be cleaner and/or more specific.
2471d8cc2 audio: Fixed logic error in SDL_OpenAudioDeviceStream.
1b03a2430 testsurround: fix order of arguments of callback
82db2b58f Renamed audio stream callback and moved the userdata parameter first
5bdad5210 Sync SDL3 wiki -> header
58c859f64 audio: Rename SDL_GetAudioStreamBinding to SDL_GetAudioStreamDevice.
efd2023a7 audio: Fixed documentation.
1e775e0ee audio: Replace SDL_CreateAndBindAudioStream with SDL_OpenAudioDeviceStream.
bd088c2f9 Revert "Clarify whether an audio function expects a physical or logical device ID"
82e481b52 Added --randmem test parameter
ea68bb802 Add some additional checks to audio_convertAudio
f8286df16 Fixed ResampleFrame_SSE doing unnecessary work
b1d63be53 Fixed audio_resampleLoss test
c191d6c30 Better Win32 transparent window support
923d612ca hidapi: sync macOS code with mainstream.
363f4fa9c avoid type redefinition errors after commit ee806597b9.
615824a80 Updated documentation now that SDL_GetAudioDevices() has been split into separate functions for output and capture devices
506a133d8 Clarify whether an audio function expects a physical or logical device ID
3b1d1e4e3 hidapi: sync the hidraw changes with mainstream
f617918e0 cmake: check linkage to libusb too, instead of libusb.h presence only.
041dbd6b5 Fixed GetResamplerAvailableOutputFrames Non-euclidean division is a pain
b49d0a607 x11: Avoid including full Vulkan headers.
4d2f9f3a3 yuv_rgb: Comment out unused code.
3c3486e2a wayland: Don't include full Vulkan headers when not necessary.
f066bbe98 x11: Don't include system headers twice.
d86d02bbb updated dynapi after SDL_GDKGetDefaultUser addition
4355f9cec Fixed warning C4389: '!=': signed/unsigned mismatch
5755de07a Fixed build warnings
0f80d47bb Fixed thread-safety warning
ee806597b Removed SDL_vulkan_internal.h from SDL_sysvideo.h
34860b932 Fixed testautomation --filter pixels_allocFreeFormat
6f8a6a31c gdk: GetBasePath should be a UTF8 version of Win32 GetBasePath
e30e5c77e Sync SDL3 wiki -> header
c0cd8c814 gdk: Add SDL_GDKGetDefaultUser, SDL_GetPrefPath implementation
106abce69 Refactored GetAudioStreamDataInternal buffer handling The final conversion step should now always go straight into the output buffer.
e44f54ec5 Avoid using hex-floats
5b696996c Added ResampleFrame_SSE
958b3cfae Tweaked and enabled audio_convertAudio test
7dbb9b65b audio_convertAccuracy: Shuffle the data in case of a bad SIMD implementation
f6a4080ff audio_resampleLoss: Add support for multiple channels
4f894e748 audio_resampleLoss: SDL_GetAudioStreamData now returns the correct length
ab83f75bb Make sure GetAudioStreamDataInternal is called with a valid length
6a73f74b6 Rebuild full ResamplerFilter (left wing + right wing) at runtime
0c15ce006 Add a missing int cast
b74ee86b1 Optimized ResampleAudio, with special cases for 1 and 2 channels This would also benefit from some SIMD, since it's just a bunch of multiply-adds
fba6e1e3d Removed ResamplerFilterDifference It takes 1 extra multiply to calculate the correct interpolation, but I think the improvement in cache locality (and binary size) outweighs that.
9f7a22fa4 Removed 64-bit handling from AudioConvertByteswap
1f5327a9f Removed future_buffer, left_padding, and right_padding from SDL_AudioStream
71ad52d6d Lowered SDL_GetAudioStreamData to 32 KB No particular reason for this number, but 1 MB was a bit silly
69aec8c91 Fixed the report format for the Razer Wolverine V2 Pro
7c2669c9d Accept key events from any source
1e9d31448 Updated to Android minSdkVersion 19 and targetSdkVersion 34
8924d0d92 Added missing function prototype for SDL_WriteS64BE()
845f3c745 Fixed mismatch between stdlib calloc() and SDL free()
fb7921173 emscriptenaudio: Fire the capture silence_callback at an interval.
5191b2054 emscriptenaudio: Don't bother undefining things about to be unreachable.
fd75a4ca0 emscriptenaudio: Deal with blocked audio devices better.
981b8a337 emscriptenaudio: Remove unnecessary functions.
c7588e426 Transparent window for Win32 + OpenGL (#8143)
f9581178d cmake: fixed a typo.
e6c878824 Fixed ResampleAudio interpolation factor calculation
498363863 Misc audio tweaks/cleanup
72d9d53de Invert the inner ResampleAudio loops to avoid doing unnecessary work
88123a510 The history buffer should always have the maximum possible padding frames
96e47f165 Clamp results of GetResampler(AvailableOutput|NeededInput)Frames
d2b9c8b80 Fixed maths in testaudiostreamdynamicresample (and just show the actual scale)
14e38b17d Removed assertions from inner ResampleAudio loop
9d413dfdc The history buffer doesn't need to be so large
2788e848f Allow resampling less than 1 frame of input
383084e0a Pre-calculate resampling rate, and use it instead of .freq in most places
40a6a445c Update resample_offset inside ResampleAudio
47fea7f06 Used fixed-point arithmetic in ResampleAudio
7bb4e806e Clear resample_offset in SDL_ClearAudioStream, not SetAudioStreamFormat Not entirely sure if ClearAudioStream is the right place, but SetAudioStreamFormat was the wrong place
b9541b9ea Improved ResampleAudio * filterindex2 was off-by-one * Generate ResamplerFilter using doubles * Transpose ResamplerFilter to improve access patterns
cdaa19869 Track offset within the current sample when resampling
d60ebb06d mouse: Ensure that the dummy default cursor is removed from the cursor list
e58c2731f mouse: Free the default cursor when destroyed
789ce17e1 audio: Don't resample in chunks for now.
cbab33482 audio: Don't call SDL_AudioStream callbacks for empty data sets.
3e1ae0c86 Clearified the libusb whitelist default logic
f4520821e Removed some unnecessary integer casts
0989b7e86 Avoid using designated initializers
c6c1e673c Optimized SDL_Convert_*_to_*_Scalar
f97b920b3 Optimized SDL_Convert_*_to_*_SSE2 Some of the SDL_Convert_F32_to_*_SSE2 do not explicitly clamp the input, but instead rely on saturating casts. Inputs very far outside the valid [-1.0, 1.0] range may produce an incorrect result, but I believe that is an acceptable trade-off.
300d1ec3e Added audio_convertAccuracy test
32cecc2ea Fixed assertion in audio_convertAudio
33f11e21e Removed assertions in AudioConvert(To|From)Float
c2f388fd8 cmake: add SDL_HIDAPI_LIBUSB_SHARED option + test on ci
371cc2d17 wayland: Remove unnecessary flag and state settings
fe85e6e75 cocoa: Send a maximized event instead of restored if a deminiaturized window is zoomed
ddddcb78c cocoa: Use the close method to hide a miniaturized window
be8c42cfd Clarify that a window being 'hidden' means that it is unmapped/ordered out
a44338cbc Fix typo in SDL_audiocvt.c
f464eb2c5 SDL_hidapi.c: change 'use_libusb_whitelist_default' into a macro.
6607a3cfa Disable cache in python http server
181d5d285 hidapi: Enable libusb support by default.
f0f15e365 hidapi: Use a whitelist for libusb when other backends are available
c3f7a7dc4 Convert audio using SDL_AUDIO_F32SYS format instead of SDL_AUDIO_F32
796713b9d xxd.py: always write \n line endings
723bcd0a8 SDL_TriggerBreakppoint for riscv arch (both 32/64) version.

git-subtree-dir: external/sdl/SDL
git-subtree-split: 399bc709b7485bab57880f8261f826f29dc0d7b2
2023-09-23 18:45:49 +02:00
12e8b5d6c4 windows -> linux ln fixes 2023-09-23 18:45:03 +02:00
f5c7261805 update nix flake to support more platforms 2023-09-16 23:54:51 +02:00
73ee0f905b added nix flake (hacky, shipping sdl was a mistake) 2023-09-15 20:06:37 +02:00
2102 changed files with 137774 additions and 68891 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
github: Green-Sky

View File

@ -8,12 +8,59 @@ on:
env:
BUILD_TYPE: Release
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
jobs:
linux-ubuntu:
timeout-minutes: 10
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Install Dependencies
run: sudo apt update && sudo apt -y install libsodium-dev
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4
- name: Determine tag name
id: tag
shell: bash
# taken from llama.cpp
run: |
SHORT_HASH="$(git rev-parse --short=7 HEAD)"
if [[ "${{ env.BRANCH_NAME }}" == "master" ]]; then
echo "name=${SHORT_HASH}" >> $GITHUB_OUTPUT
else
SAFE_NAME=$(echo "${{ env.BRANCH_NAME }}" | tr '/' '-')
echo "name=${SAFE_NAME}-${SHORT_HASH}" >> $GITHUB_OUTPUT
fi
- name: Compress artifacts
shell: bash
run: |
tar -czvf ${{ github.event.repository.name }}-${{ steps.tag.outputs.name }}-${{ runner.os }}-ubuntu20.04-x86_64.tar.gz -C ${{github.workspace}}/build/bin/ .
- uses: actions/upload-artifact@v4
with:
# TODO: simpler name?
name: ${{ github.event.repository.name }}-${{ steps.tag.outputs.name }}-${{ runner.os }}-ubuntu20.04-x86_64
# TODO: do propper packing
path: |
${{github.workspace}}/${{ github.event.repository.name }}-${{ steps.tag.outputs.name }}-${{ runner.os }}-ubuntu20.04-x86_64.tar.gz
windows:
timeout-minutes: 15
runs-on: windows-latest
runs-on: windows-2019
steps:
- uses: actions/checkout@v3
@ -32,12 +79,141 @@ jobs:
run: cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
- uses: actions/upload-artifact@v3
- name: Determine tag name
id: tag
shell: bash
# taken from llama.cpp
run: |
SHORT_HASH="$(git rev-parse --short=7 HEAD)"
if [[ "${{ env.BRANCH_NAME }}" == "master" ]]; then
echo "name=${SHORT_HASH}" >> $GITHUB_OUTPUT
else
SAFE_NAME=$(echo "${{ env.BRANCH_NAME }}" | tr '/' '-')
echo "name=${SAFE_NAME}-${SHORT_HASH}" >> $GITHUB_OUTPUT
fi
- name: Compress artifacts
shell: powershell
run: |
Compress-Archive -Path ${{github.workspace}}/build/bin/* -Destination ${{ github.event.repository.name }}-${{ steps.tag.outputs.name }}-${{ runner.os }}-msvc-x86_64.zip
- uses: actions/upload-artifact@v4
with:
name: windows_msvc_x86-64
# TODO: simpler name?
name: ${{ github.event.repository.name }}-${{ steps.tag.outputs.name }}-${{ runner.os }}-msvc-x86_64
# TODO: do propper packing
path: |
${{github.workspace}}/build/bin/
${{github.workspace}}/${{ github.event.repository.name }}-${{ steps.tag.outputs.name }}-${{ runner.os }}-msvc-x86_64.zip
windows-asan:
timeout-minutes: 15
runs-on: windows-2019
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Install Dependencies
run: vcpkg install libsodium:x64-windows-static pthreads:x64-windows-static
# setup vs env
- uses: ilammy/msvc-dev-cmd@v1
with:
arch: amd64
- name: Configure CMake
run: cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DTOMATO_ASAN=ON -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4
- name: Determine tag name
id: tag
shell: bash
# taken from llama.cpp
run: |
SHORT_HASH="$(git rev-parse --short=7 HEAD)"
if [[ "${{ env.BRANCH_NAME }}" == "master" ]]; then
echo "name=${SHORT_HASH}" >> $GITHUB_OUTPUT
else
SAFE_NAME=$(echo "${{ env.BRANCH_NAME }}" | tr '/' '-')
echo "name=${SAFE_NAME}-${SHORT_HASH}" >> $GITHUB_OUTPUT
fi
- name: Compress artifacts
shell: powershell
run: |
Compress-Archive -Path ${{github.workspace}}/build/bin/* -Destination ${{ github.event.repository.name }}-${{ steps.tag.outputs.name }}-${{ runner.os }}-msvc-asan-x86_64.zip
- uses: actions/upload-artifact@v4
with:
# TODO: simpler name?
name: ${{ github.event.repository.name }}-${{ steps.tag.outputs.name }}-${{ runner.os }}-msvc-asan-x86_64
# TODO: do propper packing
path: |
${{github.workspace}}/${{ github.event.repository.name }}-${{ steps.tag.outputs.name }}-${{ runner.os }}-msvc-asan-x86_64.zip
release:
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) }}
runs-on: ubuntu-latest
needs:
- linux-ubuntu
- windows
- windows-asan
permissions:
contents: write
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Determine tag name
id: tag
shell: bash
# taken from llama.cpp
run: |
SHORT_HASH="$(git rev-parse --short=7 HEAD)"
if [[ "${{ env.BRANCH_NAME }}" == "master" ]]; then
echo "name=${SHORT_HASH}" >> $GITHUB_OUTPUT
else
SAFE_NAME=$(echo "${{ env.BRANCH_NAME }}" | tr '/' '-')
echo "name=${SAFE_NAME}-${SHORT_HASH}" >> $GITHUB_OUTPUT
fi
- name: Download artifacts
id: download-artifact
uses: actions/download-artifact@v4
with:
path: ./artifacts/
- name: Create release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ steps.tag.outputs.name }}
shell: bash
run: |
gh release create "$tag" \
--repo="$GITHUB_REPOSITORY" \
--title="nightly ${tag#v}" \
--notes="nightly build" \
--prerelease
- name: Upload artifacts
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ steps.tag.outputs.name }}
shell: bash
run: |
ls -laR ./artifacts
gh release upload "$tag" ./artifacts/*/* \
--repo="$GITHUB_REPOSITORY"

4
.gitignore vendored
View File

@ -11,6 +11,7 @@ cmake-build-release/
*.coredump
compile_commands.json
/build*
/result*
.clangd
.cache
@ -20,3 +21,6 @@ compile_commands.json
CMakeLists.txt.user*
CMakeCache.txt
*.tox
imgui.ini

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
cmake_minimum_required(VERSION 3.14...3.24 FATAL_ERROR)
# cmake setup begin
project(tomato)
@ -6,7 +6,7 @@ project(tomato)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# defaulting to debug mode, if not specified
if(NOT CMAKE_BUILD_TYPE)
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug")
endif()
@ -18,6 +18,26 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
option(TOMATO_ASAN "Build tomato with asan (gcc/clang/msvc)" OFF)
if (TOMATO_ASAN)
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
if (NOT WIN32) # exclude mingw
link_libraries(-fsanitize=address)
#link_libraries(-fsanitize=address,undefined)
#link_libraries(-fsanitize=undefined)
message("II enabled ASAN")
else()
message("!! can not enable ASAN on this platform (gcc/clang + win)")
endif()
elseif (MSVC)
add_compile_options("/fsanitize=address")
message("II enabled ASAN")
else()
message("!! can not enable ASAN on this platform")
endif()
endif()
# external libs
add_subdirectory(./external) # before increasing warn levels, sad :(
@ -33,13 +53,8 @@ if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL
#-Wsign-conversion # Warn on sign conversions
-Wshadow # Warn if a variable declaration shadows one from a parent context
)
if (NOT WIN32)
#link_libraries(-fsanitize=address,undefined)
#link_libraries(-fsanitize=undefined)
endif()
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
if (MSVC)
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")

View File

@ -2,3 +2,7 @@
![tomato](res/icon/tomato_v1_256.png)
## Highly experimental solanaceae client with Tox built-in
![group chat](res/tomato_screenshot_group_bot_text_23-02-2024.png)

View File

@ -17,4 +17,5 @@ add_subdirectory(./imgui)
add_subdirectory(./stb)
add_subdirectory(./libwebp)
add_subdirectory(./qoi)

View File

@ -12,14 +12,14 @@ jobs:
timeout-minutes: 30
env:
IWYU: 0.18
LLVM: 14
IWYU: 0.19
LLVM: 15
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install llvm/clang
# see: https://apt.llvm.org/
run: |
@ -39,17 +39,23 @@ jobs:
git clone https://github.com/include-what-you-use/include-what-you-use.git --branch $IWYU --depth 1
mkdir include-what-you-use/build
cd include-what-you-use/build
cmake -DCMAKE_C_COMPILER=clang-$LLVM -DCMAKE_CXX_COMPILER=clang++-$LLVM -DCMAKE_INSTALL_PREFIX=./ ..
cmake -DCMAKE_C_COMPILER=clang-$LLVM \
-DCMAKE_CXX_COMPILER=clang++-$LLVM \
-DCMAKE_INSTALL_PREFIX=./ \
..
make -j4
bin/include-what-you-use --version
- name: Compile tests
working-directory: build
run: |
export PATH=$PATH:${GITHUB_WORKSPACE}/build/include-what-you-use/build/bin
cmake -DENTT_BUILD_TESTING=ON \
cmake -DCMAKE_C_COMPILER=clang-$LLVM \
-DCMAKE_CXX_COMPILER=clang++-$LLVM \
-DENTT_BUILD_TESTING=ON \
-DENTT_BUILD_BENCHMARK=ON \
-DENTT_BUILD_EXAMPLE=ON \
-DENTT_BUILD_LIB=ON \
-DENTT_BUILD_SNAPSHOT=ON \
-DCMAKE_CXX_INCLUDE_WHAT_YOU_USE="include-what-you-use;-Xiwyu;--mapping_file=${GITHUB_WORKSPACE}/entt.imp;-Xiwyu;--no_fwd_decls;-Xiwyu;--verbose=1" ..
-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

View File

@ -9,38 +9,61 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, ubuntu-20.04]
compiler:
- pkg: g++-7
exe: g++-7
- pkg: g++-8
exe: g++-8
- pkg: g++-9
exe: g++-9
- pkg: g++-10
exe: g++-10
- pkg: clang-8
exe: clang++-8
- pkg: clang-9
exe: clang++-9
- pkg: clang-10
exe: clang++-10
- pkg: clang-11
exe: clang++-11
- pkg: clang-12
exe: clang++-12
- { pkg: g++, exe: 'g++', version: 7 }
- { pkg: g++, exe: 'g++', version: 8 }
- { pkg: g++, exe: 'g++', version: 9 }
- { pkg: g++, exe: 'g++', version: 10 }
- { pkg: g++, exe: 'g++', version: 11 }
- { pkg: g++, exe: 'g++', version: 12 }
- { pkg: clang, exe: 'clang++', version: 8 }
- { pkg: clang, exe: 'clang++', version: 9 }
- { pkg: clang, exe: 'clang++', version: 10 }
- { pkg: clang, exe: 'clang++', version: 11 }
- { pkg: clang, exe: 'clang++', version: 12 }
- { pkg: clang, exe: 'clang++', version: 13 }
- { pkg: clang, exe: 'clang++', version: 14 }
exclude:
- os: ubuntu-latest
compiler: { pkg: g++, exe: 'g++', version: 7 }
- os: ubuntu-latest
compiler: { pkg: g++, exe: 'g++', version: 8 }
- os: ubuntu-latest
compiler: { pkg: g++, exe: 'g++', version: 9 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 8 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 9 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 10 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 11 }
- os: ubuntu-20.04
compiler: { pkg: g++, exe: 'g++', version: 10 }
- os: ubuntu-20.04
compiler: { pkg: g++, exe: 'g++', version: 11 }
- os: ubuntu-20.04
compiler: { pkg: g++, exe: 'g++', version: 12 }
- os: ubuntu-20.04
compiler: { pkg: clang, exe: 'clang++', version: 12 }
- os: ubuntu-20.04
compiler: { pkg: clang, exe: 'clang++', version: 13 }
- os: ubuntu-20.04
compiler: { pkg: clang, exe: 'clang++', version: 14 }
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install compiler
run: |
sudo apt update
sudo apt install -y ${{ matrix.compiler.pkg }}
sudo apt install -y ${{ matrix.compiler.pkg }}-${{ matrix.compiler.version }}
- name: Compile tests
working-directory: build
env:
CXX: ${{ matrix.compiler.exe }}
CXX: ${{ matrix.compiler.exe }}-${{ matrix.compiler.version }}
run: |
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
make -j4
@ -50,32 +73,6 @@ jobs:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4
linux-extra:
timeout-minutes: 15
strategy:
matrix:
compiler: [g++, clang++]
id_type: ["std::uint32_t", "std::uint64_t"]
cxx_std: [cxx_std_17, cxx_std_20]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Compile tests
working-directory: build
env:
CXX: ${{ matrix.compiler }}
run: |
cmake -DENTT_BUILD_TESTING=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} ..
make -j4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4
windows:
timeout-minutes: 15
@ -93,7 +90,7 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Compile tests
working-directory: build
run: |
@ -105,35 +102,12 @@ jobs:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4
windows-extra:
timeout-minutes: 15
strategy:
matrix:
id_type: ["std::uint32_t", "std::uint64_t"]
cxx_std: [cxx_std_17, cxx_std_20]
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: Compile tests
working-directory: build
run: |
cmake -DENTT_BUILD_TESTING=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} ..
cmake --build . -j 4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4
macos:
timeout-minutes: 15
runs-on: macOS-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Compile tests
working-directory: build
run: |
@ -145,23 +119,24 @@ jobs:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest --timeout 30 -C Debug -j4
macos-extra:
extra:
timeout-minutes: 15
strategy:
matrix:
os: [windows-latest, macOS-latest, ubuntu-latest]
id_type: ["std::uint32_t", "std::uint64_t"]
cxx_std: [cxx_std_17, cxx_std_20]
runs-on: macOS-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Compile tests
working-directory: build
run: |
cmake -DENTT_BUILD_TESTING=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} ..
make -j4
cmake --build . -j 4
- name: Run tests
working-directory: build
env:

View File

@ -9,11 +9,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Compile tests
working-directory: build
env:
CXXFLAGS: "--coverage -fno-inline"
CXXFLAGS: "--coverage -fno-elide-constructors -fno-inline -fno-default-inline"
CXX: g++
run: |
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
@ -30,7 +30,7 @@ jobs:
lcov -c -d . -o coverage.info
lcov -l coverage.info
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v2
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: build/coverage.info

View File

@ -15,7 +15,7 @@ jobs:
FORMULA: entt.rb
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Clone repository
working-directory: build
env:

View File

@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Compile tests
working-directory: build
env:

View File

@ -2,7 +2,7 @@
# EnTT
#
cmake_minimum_required(VERSION 3.12.4)
cmake_minimum_required(VERSION 3.15.7)
#
# Read project version
@ -31,7 +31,7 @@ endif()
message(VERBOSE "*")
message(VERBOSE "* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
message(VERBOSE "* Copyright (c) 2017-2022 Michele Caini <michele.caini@gmail.com>")
message(VERBOSE "* Copyright (c) 2017-2023 Michele Caini <michele.caini@gmail.com>")
message(VERBOSE "*")
#
@ -143,6 +143,7 @@ if(ENTT_INCLUDE_HEADERS)
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/group.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/handle.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/mixin.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/helper.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/observer.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/organizer.hpp>
@ -151,7 +152,6 @@ if(ENTT_INCLUDE_HEADERS)
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/snapshot.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/sparse_set.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/storage.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/storage_mixin.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>

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2017-2022 Michele Caini, author of EnTT
Copyright (c) 2017-2023 Michele Caini, author of EnTT
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -6,7 +6,8 @@
[![Build Status](https://github.com/skypjack/entt/workflows/build/badge.svg)](https://github.com/skypjack/entt/actions)
[![Coverage](https://codecov.io/gh/skypjack/entt/branch/master/graph/badge.svg)](https://codecov.io/gh/skypjack/entt)
[![Try online](https://img.shields.io/badge/try-online-brightgreen)](https://godbolt.org/z/zxW73f)
[![Documentation](https://img.shields.io/badge/docs-docsforge-blue)](http://entt.docsforge.com/)
[![Vcpkg port](https://img.shields.io/vcpkg/v/entt)](https://vcpkg.link/ports/entt)
[![Documentation](https://img.shields.io/badge/docs-doxygen-blue)](https://skypjack.github.io/entt/)
[![Gitter chat](https://badges.gitter.im/skypjack/entt.png)](https://gitter.im/skypjack/entt)
[![Discord channel](https://img.shields.io/discord/707607951396962417?logo=discord)](https://discord.gg/5BjPWBd)
[![Donate](https://img.shields.io/badge/donate-paypal-blue.svg)](https://www.paypal.me/skypjack)
@ -343,8 +344,8 @@ The documentation is based on [doxygen](http://www.doxygen.nl/). To build it:
$ cmake .. -DENTT_BUILD_DOCS=ON
$ make
The API reference will be created in HTML format within the directory
`build/docs/html`. To navigate it with your favorite browser:
The API reference is created in HTML format in the `build/docs/html` directory.
To navigate it with your favorite browser:
$ cd build
$ your_favorite_browser docs/html/index.html
@ -353,10 +354,7 @@ The API reference will be created in HTML format within the directory
@cond TURN_OFF_DOXYGEN
-->
The same version is also available [online](https://skypjack.github.io/entt/)
for the latest release, that is the last stable tag. If you are looking for
something more pleasing to the eye, consider reading the nice-looking version
available on [docsforge](https://entt.docsforge.com/): same documentation, much
more pleasant to read.<br/>
for the latest release, that is the last stable tag.<br/>
Moreover, there exists a [wiki](https://github.com/skypjack/entt/wiki) dedicated
to the project where users can find all related documentation pages.
<!--
@ -366,9 +364,8 @@ to the project where users can find all related documentation pages.
# Tests
To compile and run the tests, `EnTT` requires *googletest*.<br/>
`cmake` will download and compile the library before compiling anything else.
In order to build the tests, set the `CMake` option `ENTT_BUILD_TESTING` to
`ON`.
`cmake` downloads and compiles the library before compiling anything else. In
order to build the tests, set the `CMake` option `ENTT_BUILD_TESTING` to `ON`.
To build the most basic set of tests:
@ -420,7 +417,7 @@ know who has participated so far.
# License
Code and documentation Copyright (c) 2017-2022 Michele Caini.<br/>
Code and documentation Copyright (c) 2017-2023 Michele Caini.<br/>
Colorful logo Copyright (c) 2018-2021 Richard Caseres.
Code released under

View File

@ -1,27 +1,26 @@
* debugging tools (#60): the issue online already contains interesting tips on this, look at it
* work stealing job system (see #100) + mt scheduler based on const awareness for types
EXAMPLES
* filter on runtime values/variables (not only types)
* support to polymorphic types (see #859)
DOC:
* storage<void>
* custom storage/view
* examples (and credits) from @alanjfs :)
* update entity doc when the storage based model is in place
* in-place O(1) release/destroy for non-orphaned entities, out-of-sync model
TODO (high prio):
* remove the static storage from the const assure in the registry
* check natvis files (periodically :)
* resource cache: avoid using shared ptr with loader and the others
* further optimize exclusion lists in multi type views (no existence check)
* further improve the snapshot stuff, ie component functions
* use fixture for storage tests to reduce loc number and duplication as much as possible
* basic_view<...>::reach(...)
* doc: bump entities
WIP:
* get rid of observers, storage based views made them pointless - document alternatives
* add storage getter for filters to views and groups
* exploit the tombstone mechanism to allow enabling/disabling entities (see bump, compact and clear for further details)
* basic_storage::bind for cross-registry setups (see and remove todo from entity_copy.cpp)
* process scheduler: reviews, use free lists internally
* dedicated entity storage, in-place O(1) release/destroy for non-orphaned entities, out-of-sync model
* entity-only and exclude-only views (both solved with entity storage and storage<void>)
* custom allocators all over (registry, ...)
* add test for maximum number of entities reached
* deprecate non-owning groups in favor of owning views and view packs, introduce lazy owning views
* bring nested groups back in place (see bd34e7f)
* work stealing job system (see #100) + mt scheduler based on const awareness for types

View File

@ -2,8 +2,22 @@
# Doxygen configuration (documentation)
#
set(DOXY_DEPS_DIRECTORY ${EnTT_SOURCE_DIR}/deps)
FetchContent_Declare(
doxygen-awesome-css
GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css
GIT_TAG main
GIT_SHALLOW 1
)
FetchContent_GetProperties(doxygen-awesome-css)
if(NOT doxygen-awesome-css_POPULATED)
FetchContent_Populate(doxygen-awesome-css)
set(doxygen-awesome-css_INCLUDE_DIR ${doxygen-awesome-css_SOURCE_DIR})
endif()
set(DOXY_SOURCE_DIRECTORY ${EnTT_SOURCE_DIR}/src)
set(DOXY_CSS_DIRECTORY ${doxygen-awesome-css_INCLUDE_DIR})
set(DOXY_DOCS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
set(DOXY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})

View File

@ -1,4 +1,4 @@
# Doxyfile 1.9.4
# Doxyfile 1.9.6
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@ -19,7 +19,8 @@
# configuration file:
# doxygen -x [configFile]
# Use doxygen to compare the used configuration file with the template
# configuration file without replacing the environment variables:
# configuration file without replacing the environment variables or CMake type
# replacement variables:
# doxygen -x_noenv [configFile]
#---------------------------------------------------------------------------
@ -85,7 +86,7 @@ CREATE_SUBDIRS = NO
# level increment doubles the number of directories, resulting in 4096
# directories at level 8 which is the default and also the maximum value. The
# sub-directories are organized in 2 levels, the first level always has a fixed
# numer of 16 directories.
# number of 16 directories.
# Minimum value: 0, maximum value: 8, default value: 8.
# This tag requires that the tag CREATE_SUBDIRS is set to YES.
@ -557,7 +558,8 @@ HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy. If set
# to NO, these classes will be included in the various overviews. This option
# has no effect if EXTRACT_ALL is enabled.
# will also hide undocumented C++ concepts if enabled. This option has no effect
# if EXTRACT_ALL is enabled.
# The default value is: NO.
HIDE_UNDOC_CLASSES = NO
@ -595,7 +597,8 @@ INTERNAL_DOCS = NO
# Windows (including Cygwin) and MacOS, users should typically set this option
# to NO, whereas on Linux or other Unix flavors it should typically be set to
# YES.
# The default value is: system dependent.
# Possible values are: SYSTEM, NO and YES.
# The default value is: SYSTEM.
CASE_SENSE_NAMES = YES
@ -847,6 +850,14 @@ WARN_IF_INCOMPLETE_DOC = YES
WARN_NO_PARAMDOC = YES
# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
# undocumented enumeration values. If set to NO, doxygen will accept
# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
# will automatically be disabled.
# The default value is: NO.
WARN_IF_UNDOC_ENUM_VAL = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
@ -905,10 +916,21 @@ INPUT = @DOXY_SOURCE_DIRECTORY@ \
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see:
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
# See also: INPUT_FILE_ENCODING
# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
# This tag can be used to specify the character encoding of the source files
# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
# character encoding on a per file pattern basis. Doxygen will compare the file
# name with each pattern and apply the encoding instead of the default
# INPUT_ENCODING) if there is a match. The character encodings are a list of the
# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
# "INPUT_ENCODING" for further information on supported encodings.
INPUT_FILE_ENCODING =
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
# *.h) to filter out the source-files in the directories.
@ -945,7 +967,7 @@ RECURSIVE = YES
# Note that relative paths are relative to the directory from which doxygen is
# run.
EXCLUDE = @DOXY_DEPS_DIRECTORY@
EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
@ -1015,6 +1037,11 @@ IMAGE_PATH =
# code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly.
#
# Note that doxygen will use the data processed and written to standard output
# for further processing, therefore nothing else, like debug statements or used
# commands (so in case of a Windows batch file always use @echo OFF), should be
# written to standard output.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
@ -1056,6 +1083,15 @@ FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE = @PROJECT_SOURCE_DIR@/README.md
# The Fortran standard specifies that for fixed formatted Fortran code all
# characters from position 72 are to be considered as comment. A common
# extension is to allow longer lines before the automatic comment starts. The
# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
# be processed before the automatic comment starts.
# Minimum value: 7, maximum value: 10000, default value: 72.
FORTRAN_COMMENT_AFTER = 72
#---------------------------------------------------------------------------
# Configuration options related to source browsing
#---------------------------------------------------------------------------
@ -1193,10 +1229,11 @@ CLANG_DATABASE_PATH =
ALPHABETICAL_INDEX = YES
# In case all classes in a project start with a common prefix, all classes will
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
# can be used to specify a prefix (or a list of prefixes) that should be ignored
# while generating the index headers.
# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
# that should be ignored while generating the index headers. The IGNORE_PREFIX
# tag works for classes, function and member names. The entity will be placed in
# the alphabetical list under the first letter of the entity name that remains
# after removing the prefix.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX =
@ -1265,7 +1302,7 @@ HTML_FOOTER =
# obsolete.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_STYLESHEET =
HTML_STYLESHEET = @DOXY_CSS_DIRECTORY@/doxygen-awesome.css
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# cascading style sheets that are included after the standard style sheets
@ -1275,7 +1312,12 @@ HTML_STYLESHEET =
# Doxygen will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list). For an example see the documentation.
# list).
# Note: Since the styling of scrollbars can currently not be overruled in
# Webkit/Chromium, the styling will be left out of the default doxygen.css if
# one or more extra stylesheets have been specified. So if scrollbar
# customization is desired it has to be added explicitly. For an example see the
# documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET =
@ -1290,6 +1332,19 @@ HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES =
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
# should be rendered with a dark or light theme.
# Possible values are: LIGHT always generate light mode output, DARK always
# generate dark mode output, AUTO_LIGHT automatically set the mode according to
# the user preference, use light mode if no preference is set (the default),
# AUTO_DARK automatically set the mode according to the user preference, use
# dark mode if no preference is set and TOGGLE allow to user to switch between
# light and dark mode via a button.
# The default value is: AUTO_LIGHT.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE = LIGHT
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
# this color. Hue is specified as an angle on a color-wheel, see
@ -1653,17 +1708,6 @@ HTML_FORMULA_FORMAT = png
FORMULA_FONTSIZE = 10
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
# generated for formulas are transparent PNGs. Transparent PNGs are not
# supported properly for IE 6.0, but are supported on all modern browsers.
#
# Note that when changing this option you need to delete any form_*.png files in
# the HTML output directory before the changes have effect.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_TRANSPARENT = YES
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
# to create new LaTeX commands to be used in formulas as building blocks. See
# the section "Including formulas" for details.
@ -2379,26 +2423,38 @@ HAVE_DOT = YES
DOT_NUM_THREADS = 0
# When you want a differently looking font in the dot files that doxygen
# generates you can specify the font name using DOT_FONTNAME. You need to make
# sure dot is able to find the font, which can be done by putting it in a
# standard location or by setting the DOTFONTPATH environment variable or by
# setting DOT_FONTPATH to the directory containing the font.
# The default value is: Helvetica.
# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
# subgraphs. When you want a differently looking font in the dot files that
# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
# Edge and Graph Attributes specification</a> You need to make sure dot is able
# to find the font, which can be done by putting it in a standard location or by
# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
# directory containing the font. Default graphviz fontsize is 14.
# The default value is: fontname=Helvetica,fontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTNAME = Helvetica
DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10"
# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
# dot graphs.
# Minimum value: 4, maximum value: 24, default value: 10.
# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
# arrows shapes.</a>
# The default value is: labelfontname=Helvetica,labelfontsize=10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTSIZE = 10
DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10"
# By default doxygen will tell dot to use the default font as specified with
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
# the path where dot can find it using this tag.
# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
# around nodes set 'shape=plain' or 'shape=plaintext' <a
# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
# The default value is: shape=box,height=0.2,width=0.4.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
# You can set the path where dot can find font specified with fontname in
# DOT_COMMON_ATTR and others dot attributes.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTPATH =
@ -2641,18 +2697,6 @@ DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
# background. This is disabled by default, because dot on Windows does not seem
# to support this out of the box.
#
# Warning: Depending on the platform used, enabling this option may lead to
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
# read).
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_TRANSPARENT = NO
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10) support

View File

@ -17,7 +17,6 @@
* [ENTT_DISABLE_ASSERT](#entt_disable_assert)
* [ENTT_NO_ETO](#entt_no_eto)
* [ENTT_STANDARD_CPP](#entt_standard_cpp)
<!--
@endcond TURN_OFF_DOXYGEN
-->

View File

@ -9,7 +9,6 @@
* [Containers](#containers)
* [Dense map](#dense-map)
* [Dense set](#dense-set)
<!--
@endcond TURN_OFF_DOXYGEN
-->
@ -21,7 +20,7 @@ difficult to do better (although it's very easy to do worse, as many examples
available online demonstrate).<br/>
`EnTT` doesn't try in any way to replace what is offered by the standard. Quite
the opposite, given the widespread use that is made of standard containers.<br/>
However, the library also tries to fill a gap in features and functionality by
However, the library also tries to fill a gap in features and functionalities by
making available some containers initially developed for internal use.
This section of the library is likely to grow larger over time. However, for the
@ -40,7 +39,7 @@ The implementation is based on _sparse sets_ and each bucket is identified by an
implicit list within the packed array itself.
The interface is very close to its counterpart in the standard library, that is,
`std::unordered_map`.<br/>
the `std::unordered_map` class.<br/>
However, both local and non-local iterators returned by a dense map belong to
the input iterator category although they respectively model the concepts of a
_forward iterator_ type and a _random access iterator_ type.<br/>
@ -63,5 +62,5 @@ The implementation is based on _sparse sets_ and each bucket is identified by an
implicit list within the packed array itself.
The interface is in all respects similar to its counterpart in the standard
library, that is, `std::unordered_set`.<br/>
library, that is, the `std::unordered_set` class.<br/>
Therefore, there is no need to go into the API description.

View File

@ -46,25 +46,24 @@
# Introduction
`EnTT` comes with a bunch of core functionalities mostly used by the other parts
of the library itself.<br/>
Hardly users will include these features in their code, but it's worth
describing what `EnTT` offers so as not to reinvent the wheel in case of need.
of the library.<br/>
Many of these tools are also useful in everyday work. Therefore, it's worth
describing them so as not to reinvent the wheel in case of need.
# Any as in any type
`EnTT` comes with its own `any` type. It may seem redundant considering that
C++17 introduced `std::any`, but it is not (hopefully).<br/>
`EnTT` offers its own `any` type. It may seem redundant considering that C++17
introduced `std::any`, but it is not (hopefully).<br/>
First of all, the _type_ returned by an `std::any` is a const reference to an
`std::type_info`, an implementation defined class that's not something everyone
wants to see in a software. Furthermore, there is no way to connect it with the
type system of the library and therefore with its integrated RTTI support.<br/>
Note that this class is largely used internally by the library itself.
wants to see in a software. Furthermore, there is no way to bind it to the type
system of the library and therefore with its integrated RTTI support.
The API is very similar to that of its most famous counterpart, mainly because
this class serves the same purpose of being an opaque container for any type of
value.<br/>
Instances of `any` also minimize the number of allocations by relying on a well
known technique called _small buffer optimization_ and a fake vtable.
The `any` API is very similar to that of its most famous counterpart, mainly
because this class serves the same purpose of being an opaque container for any
type of value.<br/>
Instances also minimize the number of allocations by relying on a well known
technique called _small buffer optimization_ and a fake vtable.
Creating an object of the `any` type, whether empty or not, is trivial:
@ -93,8 +92,8 @@ Furthermore, an instance of `any` isn't tied to an actual type. Therefore, the
wrapper is reconfigured when it's assigned a new object of a type other than
the one it contains.
There exists also a way to directly assign a value to the variable contained by
an `entt::any`, without necessarily replacing it. This is especially useful when
There is also a way to directly assign a value to the variable contained by an
`entt::any`, without necessarily replacing it. This is especially useful when
the object is used in _aliasing mode_, as described below:
```cpp
@ -108,15 +107,15 @@ any.assign(value);
any.assign(std::move(value));
```
The `any` class will also perform a check on the type information and whether or
not the original type was copy or move assignable, as appropriate.<br/>
In all cases, the `assign` function returns a boolean value to indicate the
success or failure of the operation.
The `any` class performs a check on the type information and whether or not the
original type was copy or move assignable, as appropriate.<br/>
In all cases, the `assign` function returns a boolean value that is true in case
of success and false otherwise.
When in doubt about the type of object contained, the `type` member function of
`any` returns a const reference to the `type_info` associated with its element,
or `type_id<void>()` if the container is empty. The type is also used internally
when comparing two `any` objects:
When in doubt about the type of object contained, the `type` member function
returns a const reference to the `type_info` associated with its element, or
`type_id<void>()` if the container is empty.<br/>
The type is also used internally when comparing two `any` objects:
```cpp
if(any == empty) { /* ... */ }
@ -125,7 +124,7 @@ if(any == empty) { /* ... */ }
In this case, before proceeding with a comparison, it's verified that the _type_
of the two objects is actually the same.<br/>
Refer to the `EnTT` type system documentation for more details about how
`type_info` works and on possible risks of a comparison.
`type_info` works and the possible risks of a comparison.
A particularly interesting feature of this class is that it can also be used as
an opaque container for const and non-const references:
@ -153,22 +152,19 @@ entt::any ref = other.as_ref();
```
In this case, it doesn't matter if the original container actually holds an
object or acts already as a reference for unmanaged elements, the new instance
thus created won't create copies and will only serve as a reference for the
original item.<br/>
This means that, starting from the example above, both `ref` and `other` will
point to the same object, whether it's initially contained in `other` or already
an unmanaged element.
object or is as a reference for unmanaged elements already. The new instance
thus created doesn't create copies and only serves as a reference for the
original item.
As a side note, it's worth mentioning that, while everything works transparently
when it comes to non-const references, there are some exceptions when it comes
to const references.<br/>
It's worth mentioning that, while everything works transparently when it comes
to non-const references, there are some exceptions when it comes to const
references.<br/>
In particular, the `data` member function invoked on a non-const instance of
`any` that wraps a const reference will return a null pointer in all cases.
`any` that wraps a const reference returns a null pointer in all cases.
To cast an instance of `any` to a type, the library offers a set of `any_cast`
functions in all respects similar to their most famous counterparts.<br/>
The only difference is that, in the case of `EnTT`, these won't raise exceptions
The only difference is that, in the case of `EnTT`, they won't raise exceptions
but will only trigger an assert in debug mode, otherwise resulting in undefined
behavior in case of misuse in release mode.
@ -188,31 +184,23 @@ using my_any = entt::basic_any<sizeof(double[4])>;
This feature, in addition to allowing the choice of a size that best suits the
needs of an application, also offers the possibility of forcing dynamic creation
of objects during construction.<br/>
In other terms, if the size is 0, `any` avoids the use of any optimization and
always dynamically allocates objects (except for aliasing cases).
Note that the size of the internal storage as well as the alignment requirements
are directly part of the type and therefore contribute to define different types
that won't be able to interoperate with each other.
In other terms, if the size is 0, `any` suppresses the small buffer optimization
and always dynamically allocates objects (except for aliasing cases).
## Alignment requirement
The alignment requirement is optional and by default the most stringent (the
largest) for any object whose size is at most equal to the one provided.<br/>
The `basic_any` class template inspects the alignment requirements in each case,
even when not provided and may decide not to use the small buffer optimization
in order to meet them.
The alignment requirement is provided as an optional second parameter following
the desired size for the internal storage:
It's provided as an optional second parameter following the desired size for the
internal storage:
```cpp
using my_any = entt::basic_any<sizeof(double[4]), alignof(double[4])>;
```
Note that the alignment requirements as well as the size of the internal storage
are directly part of the type and therefore contribute to define different types
that won't be able to interoperate with each other.
The `basic_any` class template inspects the alignment requirements in each case,
even when not provided and may decide not to use the small buffer optimization
in order to meet them.
# Compressed pair
@ -225,8 +213,8 @@ is more important than having some cool and probably useless feature.
Although the API is very close to that of `std::pair` (apart from the fact that
the template parameters are inferred from the constructor and therefore there is
no` entt::make_compressed_pair`), the major difference is that `first` and
`second` are functions for implementation needs:
no `entt::make_compressed_pair`), the major difference is that `first` and
`second` are functions for implementation requirements:
```cpp
entt::compressed_pair pair{0, 3.};
@ -239,16 +227,15 @@ intuition. At the end of the day, it's just a pair and nothing more.
# Enum as bitmask
Sometimes it's useful to be able to use enums as bitmasks. However, enum classes
aren't really suitable for the purpose out of the box. Main problem is that they
don't convert implicitly to their underlying type.<br/>
All that remains is to make a choice between using old-fashioned enums (with all
their problems that I don't want to discuss here) or writing _ugly_ code.
aren't really suitable for the purpose. Main problem is that they don't convert
implicitly to their underlying type.<br/>
The choice is then between using old-fashioned enums (with all their problems
that I don't want to discuss here) or writing _ugly_ code.
Fortunately, there is also a third way: adding enough operators in the global
scope to treat enum classes as bitmask transparently.<br/>
The ultimate goal is to be able to write code like the following (or maybe
something more meaningful, but this should give a grasp and remain simple at the
same time):
scope to treat enum classes as bitmasks transparently.<br/>
The ultimate goal is to write code like the following (or maybe something more
meaningful, but this should give a grasp and remain simple at the same time):
```cpp
enum class my_flag {
@ -261,11 +248,11 @@ const my_flag flags = my_flag::enabled;
const bool is_enabled = !!(flags & my_flag::enabled);
```
The problem with adding all operators to the global scope is that these will
come into play even when not required, with the risk of introducing errors that
are difficult to deal with.<br/>
The problem with adding all operators to the global scope is that these come
into play even when not required, with the risk of introducing errors that are
difficult to deal with.<br/>
However, C++ offers enough tools to get around this problem. In particular, the
library requires users to register all enum classes for which bitmask support
library requires users to register the enum classes for which bitmask support
should be enabled:
```cpp
@ -276,7 +263,7 @@ struct entt::enum_as_bitmask<my_flag>
```
This is handy when dealing with enum classes defined by third party libraries
and over which the users have no control. However, it's also verbose and can be
and over which the user has no control. However, it's also verbose and can be
avoided by adding a specific value to the enum class itself:
```cpp
@ -289,23 +276,21 @@ enum class my_flag {
```
In this case, there is no need to specialize the `enum_as_bitmask` traits, since
`EnTT` will automatically detect the flag and enable the bitmask support.<br/>
Once the enum class has been registered (in one way or the other) all the most
common operators will be available, such as `&`, `|` but also `&=` and `|=`.
`EnTT` automatically detects the flag and enables the bitmask support.<br/>
Once the enum class is registered (in one way or the other), the most common
operators such as `&`, `|` but also `&=` and `|=` are available for use.
Refer to the official documentation for the full list of operators.
# Hashed strings
A hashed string is a zero overhead unique identifier. Users can use
human-readable identifiers in the codebase while using their numeric
counterparts at runtime, thus without affecting performance.<br/>
Hashed strings are human-readable identifiers in the codebase that turn into
numeric values at runtime, thus without affecting performance.<br/>
The class has an implicit `constexpr` constructor that chews a bunch of
characters. Once created, all what one can do with it is getting back the
original string through the `data` member function or converting the instance
into a number.<br/>
The good part is that a hashed string can be used wherever a constant expression
is required and no _string-to-number_ conversion will take place at runtime if
used carefully.
characters. Once created, one can get the original string by means of the `data`
member function or convert the instance into a number.<br/>
A hashed string is well suited wherever a constant expression is required. No
_string-to-number_ conversion will take place at runtime if used carefully.
Example of use:
@ -318,19 +303,18 @@ auto resource = load(entt::hashed_string{"gui/background"});
```
There is also a _user defined literal_ dedicated to hashed strings to make them
more user-friendly:
more _user-friendly_:
```cpp
using namespace entt::literals;
constexpr auto str = "text"_hs;
```
To use it, remember that all user defined literals in `EnTT` are enclosed in the
`entt::literals` namespace. Therefore, the entire namespace or selectively the
literal of interest must be explicitly included before each use, a bit like
`std::literals`.<br/>
Finally, in case users need to create hashed strings at runtime, this class also
offers the necessary functionalities:
User defined literals in `EnTT` are enclosed in the `entt::literals` namespace.
Therefore, the entire namespace or selectively the literal of interest must be
explicitly included before each use, a bit like `std::literals`.<br/>
The class also offers the necessary functionalities to create hashed strings at
runtime:
```cpp
std::string orig{"text"};
@ -343,16 +327,14 @@ const auto hash = entt::hashed_string::value(orig.c_str());
```
This possibility shouldn't be exploited in tight loops, since the computation
takes place at runtime and no longer at compile-time and could therefore impact
takes place at runtime and no longer at compile-time. It could therefore affect
performance to some degrees.
## Wide characters
The hashed string has a design that is close to that of an `std::basic_string`.
It means that `hashed_string` is nothing more than an alias for
`basic_hashed_string<char>`. For those who want to use the C++ type for wide
character representation, there exists also the alias `hashed_wstring` for
`basic_hashed_string<wchar_t>`.<br/>
The `hashed_string` class is an alias for `basic_hashed_string<char>`. To use
the C++ type for wide character representations, there exists also the alias
`hashed_wstring` for `basic_hashed_string<wchar_t>`.<br/>
In this case, the user defined literal to use to create hashed strings on the
fly is `_hws`:
@ -360,16 +342,15 @@ fly is `_hws`:
constexpr auto str = L"text"_hws;
```
Note that the hash type of the `hashed_wstring` is the same of its counterpart.
The hash type of `hashed_wstring` is the same as its counterpart.
## Conflicts
The hashed string class uses internally FNV-1a to compute the numeric
counterpart of a string. Because of the _pigeonhole principle_, conflicts are
possible. This is a fact.<br/>
The hashed string class uses FNV-1a internally to hash strings. Because of the
_pigeonhole principle_, conflicts are possible. This is a fact.<br/>
There is no silver bullet to solve the problem of conflicts when dealing with
hashing functions. In this case, the best solution seemed to be to give up.
That's all.<br/>
hashing functions. In this case, the best solution is likely to give up. That's
all.<br/>
After all, human-readable unique identifiers aren't something strictly defined
and over which users have not the control. Choosing a slightly different
identifier is probably the best solution to make the conflict disappear in this
@ -377,8 +358,8 @@ case.
# Iterators
Writing and working with iterators isn't always easy and more often than not
leads to duplicated code.<br/>
Writing and working with iterators isn't always easy. More often than not it
also leads to duplicated code.<br/>
`EnTT` tries to overcome this problem by offering some utilities designed to
make this hard work easier.
@ -431,7 +412,7 @@ user.
## Iterable adaptor
Typically, a container class provides `begin` and `end` member functions (with
their const counterparts) to be iterated by the user.<br/>
their const counterparts) for iteration.<br/>
However, it can happen that a class offers multiple iteration methods or allows
users to iterate different sets of _elements_.
@ -452,8 +433,8 @@ by returning an iterable object for the purpose.
There are a handful of tools within `EnTT` to interact with memory in one way or
another.<br/>
Some are geared towards simplifying the implementation of (internal or external)
allocator aware containers. Others, on the other hand, are designed to help the
developer with everyday problems.
allocator aware containers. Others are designed to help the developer with
everyday problems.
The former are very specific and for niche problems. These are tools designed to
unwrap fancy or plain pointers (`to_address`) or to help forget the meaning of
@ -481,7 +462,7 @@ modulus and for this reason preferred in many areas.
A nasty thing in C++ (at least up to C++20) is the fact that shared pointers
support allocators while unique pointers don't.<br/>
There is a proposal at the moment that also shows among the other things how
There is a proposal at the moment that also shows (among the other things) how
this can be implemented without any compiler support.
The `allocate_unique` function follows this proposal, making a virtue out of
@ -498,13 +479,16 @@ the same feature.
# Monostate
The monostate pattern is often presented as an alternative to a singleton based
configuration system. This is exactly its purpose in `EnTT`. Moreover, this
implementation is thread safe by design (hopefully).<br/>
Keys are represented by hashed strings, values are basic types like `int`s or
`bool`s. Values of different types can be associated to each key, even more than
one at a time. Because of this, users must pay attention to use the same type
both during an assignment and when they try to read back their data. Otherwise,
they will probably incur in unexpected results.
configuration system.<br/>
This is exactly its purpose in `EnTT`. Moreover, this implementation is thread
safe by design (hopefully).
Keys are integral values (easily obtained by hashed strings), values are basic
types like `int`s or `bool`s. Values of different types can be associated with
each key, even more than one at a time.<br/>
Because of this, one should pay attention to use the same type both during an
assignment and when trying to read back the data. Otherwise, there is the risk
to incur in unexpected results.
Example of use:
@ -542,17 +526,10 @@ Basically, the whole system relies on a handful of classes. In particular:
auto index = entt::type_index<a_type>::value();
```
The returned value isn't guaranteed to be stable across different runs.
The returned value isn't guaranteed to be stable across different runs.<br/>
However, it can be very useful as index in associative and unordered
associative containers or for positional accesses in a vector or an array.
So as not to conflict with the other tools available, the `family` class isn't
used to generate these indexes. Therefore, the numeric identifiers returned by
the two tools may differ.<br/>
On the other hand, this leaves users with full powers over the `family` class
and therefore the generation of custom runtime sequences of indices for their
own purposes, if necessary.
An external generator can also be used if needed. In fact, `type_index` can be
specialized by type and is also _sfinae-friendly_ in order to allow more
refined specializations such as:
@ -566,7 +543,7 @@ Basically, the whole system relies on a handful of classes. In particular:
};
```
Note that indexes **must** still be generated sequentially in this case.<br/>
Indexes **must** be sequentially generated in this case.<br/>
The tool is widely used within `EnTT`. Generating indices not sequentially
would break an assumption and would likely lead to undesired behaviors.
@ -583,14 +560,14 @@ Basically, the whole system relies on a handful of classes. In particular:
This function **can** use non-standard features of the language for its own
purposes. This makes it possible to provide compile-time identifiers that
remain stable across different runs.<br/>
In all cases, users can prevent the library from using these features by means
of the `ENTT_STANDARD_CPP` definition. In this case, there is no guarantee
that identifiers remain stable across executions. Moreover, they are generated
Users can prevent the library from using these features by means of the
`ENTT_STANDARD_CPP` definition. In this case, there is no guarantee that
identifiers remain stable across executions. Moreover, they are generated
at runtime and are no longer a compile-time thing.
As for `type_index`, also `type_hash` is a _sfinae-friendly_ class that can be
specialized in order to customize its behavior globally or on a per-type or
per-traits basis.
As it happens with `type_index`, also `type_hash` is a _sfinae-friendly_ class
that can be specialized in order to customize its behavior globally or on a
per-type or per-traits basis.
* The name associated with a given type:
@ -598,10 +575,9 @@ Basically, the whole system relies on a handful of classes. In particular:
auto name = entt::type_name<a_type>::value();
```
The name associated with a type is extracted from some information generally
made available by the compiler in use. Therefore, it may differ depending on
the compiler and may be empty in the event that this information isn't
available.<br/>
This value is extracted from some information generally made available by the
compiler in use. Therefore, it may differ depending on the compiler and may be
empty in the event that this information isn't available.<br/>
For example, given the following class:
```cpp
@ -612,21 +588,20 @@ Basically, the whole system relies on a handful of classes. In particular:
when MSVC is in use.<br/>
Most of the time the name is also retrieved at compile-time and is therefore
always returned through an `std::string_view`. Users can easily access it and
modify it as needed, for example by removing the word `struct` to standardize
the result. `EnTT` won't do this for obvious reasons, since it requires
copying and creating a new string potentially at runtime.
modify it as needed, for example by removing the word `struct` to normalize
the result. `EnTT` doesn't do this for obvious reasons, since it would be
creating a new string at runtime otherwise.
This function **can** use non-standard features of the language for its own
purposes. Users can prevent the library from using non-standard features by
means of the `ENTT_STANDARD_CPP` definition. In this case, the name will be
empty by default.
purposes. Users can prevent the library from using these features by means of
the `ENTT_STANDARD_CPP` definition. In this case, the name is just empty.
As for `type_index`, also `type_name` is a _sfinae-friendly_ class that can be
specialized in order to customize its behavior globally or on a per-type or
per-traits basis.
As it happens with `type_index`, also `type_name` is a _sfinae-friendly_ class
that can be specialized in order to customize its behavior globally or on a
per-type or per-traits basis.
These are then combined into utilities that aim to offer an API that is somewhat
similar to that offered by the language.
similar to that made available by the standard library.
### Type info
@ -708,8 +683,8 @@ In fact, although this is quite rare, it's not entirely excluded.
Another case where two types are assigned the same identifier is when classes
from different contexts (for example two or more libraries loaded at runtime)
have the same fully qualified name. In this case, also `type_name` will return
the same value for the two types.<br/>
have the same fully qualified name. In this case, `type_name` returns the same
value for the two types.<br/>
Fortunately, there are several easy ways to deal with this:
* The most trivial one is to define the `ENTT_STANDARD_CPP` macro. Runtime
@ -739,9 +714,9 @@ offered by this module.
### Size of
The standard operator `sizeof` complains when users provide it for example with
function or incomplete types. On the other hand, it's guaranteed that its result
is always nonzero, even if applied to an empty class type.<br/>
The standard operator `sizeof` complains if users provide it with functions or
incomplete types. On the other hand, it's guaranteed that its result is always
non-zero, even if applied to an empty class type.<br/>
This small class combines the two and offers an alternative to `sizeof` that
works under all circumstances, returning zero if the type isn't supported:
@ -777,8 +752,8 @@ A utility to easily transfer the constness of a type to another type:
using type = entt::constness_as_t<dst_type, const src_type>;
```
The trait is subject to the rules of the language. Therefore, for example,
transferring constness between references won't give the desired effect.
The trait is subject to the rules of the language. For example, _transferring_
constness between references won't give the desired effect.
### Member class type
@ -798,7 +773,7 @@ A utility to quickly find the n-th argument of a function, member function or
data member (for blind operations on opaque types):
```cpp
using type = entt::nt_argument_t<1u, &clazz::member>;
using type = entt::nth_argument_t<1u, &clazz::member>;
```
Disambiguation of overloaded functions is the responsibility of the user, should
@ -825,8 +800,8 @@ registry.emplace<enemy_tag>(entity);
### Tag
Since `id_type` is very important and widely used in `EnTT`, there is a more
user-friendly shortcut for the creation of integral constants based on it.<br/>
Type `id_type` is very important and widely used in `EnTT`. Therefore, there is
a more user-friendly shortcut for the creation of constants based on it.<br/>
This shortcut is the alias template `entt::tag`.
If used in combination with hashed strings, it helps to use human-readable names
@ -918,8 +893,8 @@ Perhaps a bit ugly to see in a codebase but it gets the job done at least.
## Runtime generator
To generate sequential numeric identifiers at runtime, `EnTT` offers the
`family` class template:
The `family` class template helps to generate sequential numeric identifiers for
types at runtime:
```cpp
// defines a custom generator
@ -936,8 +911,8 @@ numeric identifier for the given type.<br/>
The generator is customizable, so as to get different _sequences_ for different
purposes if needed.
Please, note that identifiers aren't guaranteed to be stable across different
runs. Indeed it mostly depends on the flow of execution.
Identifiers aren't guaranteed to be stable across different runs. Indeed it
mostly depends on the flow of execution.
# Utilities

File diff suppressed because it is too large Load Diff

View File

@ -193,7 +193,7 @@ to mitigate the problem makes it manageable.
## Which functions trigger which signals
The `registry` class offers three signals that are emitted following specific
Storage classes offer three _signals_ that are emitted following specific
operations. Maybe not everyone knows what these operations are, though.<br/>
If this isn't clear, below you can find a _vademecum_ for this purpose:

View File

@ -14,7 +14,6 @@
* [Fake resources and order of execution](#fake-resources-and-order-of-execution)
* [Sync points](#sync-points)
* [Execution graph](#execution-graph)
<!--
@endcond TURN_OFF_DOXYGEN
-->
@ -23,15 +22,15 @@
`EnTT` doesn't aim to offer everything one needs to work with graphs. Therefore,
anyone looking for this in the _graph_ submodule will be disappointed.<br/>
Quite the opposite is true. This submodule is minimal and contains only the data
structures and algorithms strictly necessary for the development of some tools
such as the _flow builder_.
Quite the opposite is true though. This submodule is minimal and contains only
the data structures and algorithms strictly necessary for the development of
some tools such as the _flow builder_.
# Data structures
As anticipated in the introduction, the aim isn't to offer all possible data
structures suitable for representing and working with graphs. Many will likely
be added or refined over time, however I want to discourage anyone expecting
be added or refined over time. However I want to discourage anyone expecting
tight scheduling on the subject.<br/>
The data structures presented in this section are mainly useful for the
development and support of some tools which are also part of the same submodule.
@ -49,7 +48,7 @@ The `directed_tag` type _creates_ the graph as directed. There is also an
`undirected_tag` counterpart which creates it as undirected.<br/>
The interface deviates slightly from the typical double indexing of C and offers
an API that is perhaps more familiar to a C++ programmer. Therefore, the access
and modification of an element will take place via the `contains`, `insert` and
and modification of an element takes place via the `contains`, `insert` and
`erase` functions rather than a double call to an `operator[]`:
```cpp
@ -60,14 +59,14 @@ if(adjacency_matrix.contains(0u, 1u)) {
}
```
Both `insert` and` erase` are idempotent functions which have no effect if the
Both `insert` and` erase` are _idempotent_ functions which have no effect if the
element already exists or has already been deleted.<br/>
The first one returns an `std::pair` containing the iterator to the element and
a boolean value indicating whether the element has been inserted or was already
present. The second one instead returns the number of deleted elements (0 or 1).
a boolean value indicating whether the element was newly inserted or not. The
second one returns the number of deleted elements (0 or 1).
An adjacency matrix must be initialized with the number of elements (vertices)
when constructing it but can also be resized later using the `resize` function:
An adjacency matrix is initialized with the number of elements (vertices) when
constructing it but can also be resized later using the `resize` function:
```cpp
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
@ -82,8 +81,8 @@ for(auto &&vertex: adjacency_matrix.vertices()) {
}
```
Note that the same result can be obtained with the following snippet, since the
vertices are unsigned integral values:
The same result is obtained with the following snippet, since the vertices are
plain unsigned integral values:
```cpp
for(auto last = adjacency_matrix.size(), pos = {}; pos < last; ++pos) {
@ -93,8 +92,8 @@ for(auto last = adjacency_matrix.size(), pos = {}; pos < last; ++pos) {
As for visiting the edges, a few functions are available.<br/>
When the purpose is to visit all the edges of a given adjacency matrix, the
`edges` function returns an iterable object that can be used to get them as
pairs of vertices:
`edges` function returns an iterable object that is used to get them as pairs of
vertices:
```cpp
for(auto [lhs, rhs]: adjacency_matrix.edges()) {
@ -102,8 +101,8 @@ for(auto [lhs, rhs]: adjacency_matrix.edges()) {
}
```
On the other hand, if the goal is to visit all the in- or out-edges of a given
vertex, the `in_edges` and `out_edges` functions are meant for that:
If the goal is to visit all the in- or out-edges of a given vertex instead, the
`in_edges` and `out_edges` functions are meant for that:
```cpp
for(auto [lhs, rhs]: adjacency_matrix.out_edges(3u)) {
@ -111,11 +110,11 @@ for(auto [lhs, rhs]: adjacency_matrix.out_edges(3u)) {
}
```
As might be expected, these functions expect the vertex to visit (that is, to
return the in- or out-edges for) as an argument.<br/>
Both the functions expect the vertex to visit (that is, to return the in- or
out-edges for) as an argument.<br/>
Finally, the adjacency matrix is an allocator-aware container and offers most of
the functionality one would expect from this type of containers, such as `clear`
or 'get_allocator` and so on.
the functionalities one would expect from this type of containers, such as
`clear` or 'get_allocator` and so on.
## Graphviz dot language
@ -129,19 +128,19 @@ std::ostringstream output{};
entt::dot(output, adjacency_matrix);
```
However, there is also the option of providing a callback to which the vertices
are passed and which can be used to add (`dot`) properties to the output from
time to time:
It's also possible to provide a callback to which the vertices are passed and
which can be used to add (`dot`) properties to the output as needed:
```cpp
std::ostringstream output{};
entt::dot(output, adjacency_matrix, [](auto &output, auto vertex) {
out << "label=\"v\"" << vertex << ",shape=\"box\"";
});
```
This second mode is particularly convenient when the user wants to associate
data managed externally to the graph being converted.
externally managed data to the graph being converted.
# Flow builder
@ -155,42 +154,42 @@ specified.<br/>
Most of the functions in the API also return the flow builder itself, according
to what is the common sense API when it comes to builder classes.
Once all tasks have been registered and resources assigned to them, an execution
graph in the form of an adjacency matrix is returned to the user.<br/>
Once all tasks are registered and resources assigned to them, an execution graph
in the form of an adjacency matrix is returned to the user.<br/>
This graph contains all the tasks assigned to the flow builder in the form of
_vertices_. The _vertex_ itself can be used as an index to get the identifier
passed during registration.
_vertices_. The _vertex_ itself is used as an index to get the identifier passed
during registration.
## Tasks and resources
Although these terms are used extensively in the documentation, the flow builder
has no real concept of tasks and resources.<br/>
This class works mainly with _identifiers_, that is, values of type `id_type`.
That is, both tasks and resources are identified by integral values.<br/>
In other terms, both tasks and resources are identified by integral values.<br/>
This allows not to couple the class itself to the rest of the library or to any
particular data structure. On the other hand, it requires the user to keep track
of the association between identifiers and operations or actual data.
Once a flow builder has been created (which requires no constructor arguments),
the first thing to do is to bind a task. This will indicate to the builder who
intends to consume the resources that will be specified immediately after:
Once a flow builder is created (which requires no constructor arguments), the
first thing to do is to bind a task. This tells to the builder _who_ intends to
consume the resources that are specified immediately after:
```cpp
entt::flow builder{};
builder.bind("task_1"_hs);
```
Note that the example uses the `EnTT` hashed string to generate an identifier
for the task.<br/>
Indeed, the use of `id_type` as an identifier type is not by accident. In fact,
The example uses the `EnTT` hashed string to generate an identifier for the
task.<br/>
Indeed, the use of `id_type` as an identifier type isn't by accident. In fact,
it matches well with the internal hashed string class. Moreover, it's also the
same type returned by the hash function of the internal RTTI system, in case the
user wants to rely on that.<br/>
However, being an integral value, it leaves the user full freedom to rely on his
own tools if he deems it necessary.
own tools if necessary.
Once a task has been associated with the flow builder, it can be assigned
read-only or read-write resources, as appropriate:
Once a task is associated with the flow builder, it's also assigned read-only or
read-write resources as appropriate:
```cpp
builder
@ -203,13 +202,87 @@ builder
As mentioned, many functions return the builder itself and it's therefore easy
to concatenate the different calls.<br/>
Also in the case of resources, these are identified by numeric values of type
Also in the case of resources, they are identified by numeric values of type
`id_type`. As above, the choice is not entirely random. This goes well with the
tools offered by the library while leaving room for maximum flexibility.
Finally, both the `ro` and` rw` functions also offer an overload that accepts a
pair of iterators, so that one can pass a range of resources in one go.
### Rebinding
The `flow` class is resource based rather than task based. This means that graph
generation is driven by resources and not by the order of _appearance_ of tasks
during flow definition.<br/>
Although this concept is particularly important, it's almost irrelevant for the
vast majority of cases. However, it becomes relevant when _rebinding_ resources
or tasks.
In fact, nothing prevents rebinding elements to a flow.<br/>
However, the behavior changes slightly from case to case and has some nuances
that it's worth knowing about.
Directly rebinding a resource without the task being replaced trivially results
in the task's access mode for that resource being updated:
```cpp
builder.bind("task"_hs).rw("resource"_hs).ro("resource"_hs)
```
In this case, the resource is accessed in read-only mode, regardless of the
first call to `rw`.<br/>
Behind the scenes, the call doesn't actually _replace_ the previous one but is
queued after it instead, overwriting it when generating the graph. Thus, a large
number of resource rebindings may even impact processing times (very difficult
to observe but theoretically possible).
Rebinding resources and also combining it with changes to tasks has far more
implications instead.<br/>
As mentioned, graph generation takes place starting from resources and not from
tasks. Therefore, the result may not be as expected:
```cpp
builder
.bind("task_1"_hs)
.ro("resource"_hs)
.bind("task_2"_hs)
.ro("resource"_hs)
.bind("task_1"_hs)
.rw("resource"_hs);
```
What happens here is that the resource first _sees_ a read-only access request
from the first task, then a read-write request from the second task and finally
a new read-only request from the first task.<br/>
Although this definition would probably be counted as an error, the resulting
graph may be unexpected. This in fact consists of a single edge outgoing from
the second task and directed to the first task.<br/>
To intuitively understand what happens, it's enough to think of the fact that a
task never has an edge pointing to itself.
While not obvious, this approach has its pros and cons like any other solution.
For example, creating loops is actually simple in the context of resource-based
graph generations:
```cpp
builder
.bind("task_1"_hs)
.rw("resource"_hs)
.bind("task_2"_hs)
.rw("resource"_hs)
.bind("task_1"_hs)
.rw("resource"_hs);
```
As expected, this definition leads to the creation of two edges that define a
loop between the two tasks.
As a general rule, rebinding resources and tasks is highly discouraged because
it could lead to subtle bugs if users don't know what they're doing.<br/>
However, once the mechanisms of resource-based graph generation are understood,
it can offer to the expert user a flexibility and a range of possibilities
otherwise inaccessible.
## Fake resources and order of execution
The flow builder doesn't offer the ability to specify when a task should execute
@ -217,10 +290,10 @@ before or after another task.<br/>
In fact, the order of _registration_ on the resources also determines the order
in which the tasks are processed during the generation of the execution graph.
However, there is a way to force the execution order of two processes.<br/>
However, there is a way to _force_ the execution order of two processes.<br/>
Briefly, since accessing a resource in opposite modes requires sequential rather
than parallel scheduling, it's possible to make use of fake resources to force
the order execution:
than parallel scheduling, it's possible to make use of fake resources to rule on
the execution order:
```cpp
builder
@ -235,10 +308,10 @@ builder
.ro("fake"_hs)
```
This snippet forces the execution of `task_2` and `task_3` **after** `task_1`.
This is due to the fact that the latter sets a read-write requirement on a fake
This snippet forces the execution of `task_1` **before** `task_2` and `task_3`.
This is due to the fact that the former sets a read-write requirement on a fake
resource that the other tasks also want to access in read-only mode.<br/>
Similarly, it's possible to force a task to run after a certain group:
Similarly, it's possible to force a task to run **after** a certain group:
```cpp
builder
@ -261,7 +334,7 @@ others tasks.
Sometimes it's useful to assign the role of _sync point_ to a node.<br/>
Whether it accesses new resources or is simply a watershed, the procedure for
assigning this role to a vertex is always the same: first it's tied to the flow
assigning this role to a vertex is always the same. First it's tied to the flow
builder, then the `sync` function is invoked:
```cpp
@ -283,7 +356,7 @@ all specified constraints to return the best scheduling for the vertices:
entt::adjacency_matrix<entt::directed_tag> graph = builder.graph();
```
The search for the main vertices, that is those without in-edges, is usually the
Searching for the main vertices (that is, those without in-edges) is usually the
first thing required:
```cpp
@ -294,6 +367,6 @@ for(auto &&vertex: graph) {
}
```
Starting from them, using the other functions appropriately (such as `out_edges`
to retrieve the children of a given task or `edges` to access their identifiers)
it will be possible to instantiate an execution graph.
Then it's possible to instantiate an execution graph by means of other functions
such as `out_edges` to retrieve the children of a given task or `edges` to get
the identifiers.

View File

@ -19,14 +19,12 @@
general and on GNU/Linux when default visibility was set to hidden. The
limitation was mainly due to a custom utility used to assign unique, sequential
identifiers with different types.<br/>
Fortunately, nowadays using `EnTT` across boundaries is much easier.
Fortunately, nowadays `EnTT` works smoothly across boundaries.
## Smooth until proven otherwise
Many classes in `EnTT` make extensive use of type erasure for their purposes.
This isn't a problem on itself (in fact, it's the basis of an API so convenient
to use). However, a way is needed to recognize the objects whose type has been
erased on the other side of a boundary.<br/>
This raises the need to identify objects whose type has been erased.<br/>
The `type_hash` class template is how identifiers are generated and thus made
available to the rest of the library. In general, this class doesn't arouse much
interest. The only exception is when a conflict between identifiers occurs
@ -36,13 +34,13 @@ The section dedicated to `type_info` contains all the details to get around the
issue in a concise and elegant way. Please refer to the specific documentation.
When working with linked libraries, compile definitions `ENTT_API_EXPORT` and
`ENTT_API_IMPORT` can be used where there is a need to import or export symbols,
so as to make everything work nicely across boundaries.<br/>
`ENTT_API_IMPORT` are to import or export symbols, so as to make everything work
nicely across boundaries.<br/>
On the other hand, everything should run smoothly when working with plugins or
shared libraries that don't export any symbols.
For anyone who needs more details, the test suite contains multiple examples
covering the most common cases (see the `lib` directory for all details).<br/>
For those who need more details, the test suite contains many examples covering
the most common cases (see the `lib` directory for all details).<br/>
It goes without saying that it's impossible to cover **all** possible cases.
However, what is offered should hopefully serve as a basis for all of them.
@ -70,8 +68,8 @@ entt::locator<entt::meta_ctx>::reset(handle);
From now on, both spaces refer to the same context and on it are attached all
new meta types, no matter where they are created.<br/>
Note that resetting the main context doesn't also propagate changes across
boundaries. In other words, resetting a context results in the decoupling of the
Note that _replacing_ the main context doesn't also propagate changes across
boundaries. In other words, replacing a context results in the decoupling of the
two sides and therefore a divergence in the contents.
## Memory Management

View File

@ -1,5 +1,22 @@
# EnTT in Action
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [EnTT in Action](#entt-in-action)
* [Games](#games)
* [Engines and the like](#engines-and-the-like)
* [Articles, videos and blog posts](#articles-videos-and-blog-posts)
* [Any Other Business](#any-other-business)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
`EnTT` is widely used in private and commercial applications. I cannot even
mention most of them because of some signatures I put on some documents time
ago. Fortunately, there are also people who took the time to implement open
@ -7,13 +24,18 @@ source projects based on `EnTT` and didn't hold back when it came to documenting
them.
Below an incomplete list of games, applications and articles that can be used as
a reference. Where I put the word _apparently_ means that the use of `EnTT` is
documented but the authors didn't make explicit announcements or contacted me
directly.
a reference.<br/>
Where I put the word _apparently_ means that the use of `EnTT` is documented but
the authors didn't make explicit announcements or contacted me directly.
I hope this list can grow much more in the future:
If you know of other resources out there that are about `EnTT`, feel free to
open an issue or a PR and I'll be glad to add them to this page.<br/>
I hope the following lists can grow much more in the future.
# EnTT in Action
## Games
* Games:
* [Minecraft](https://minecraft.net/en-us/attribution/) by
[Mojang](https://mojang.com/): of course, **that** Minecraft, see the
open source attributions page for more details.
@ -103,7 +125,8 @@ I hope this list can grow much more in the future:
* [Confetti Party](https://github.com/hexerei/entt-confetti): C++ sample
application as a starting point using `EnTT` and `SDL2`.
* Engines and the like:
## Engines and the like:
* [Aether Engine](https://hadean.com/spatial-simulation/)
[v1.1+](https://docs.hadean.com/v1.1/Licenses/) by
[Hadean](https://hadean.com/): a library designed for spatially partitioning
@ -168,8 +191,19 @@ I hope this list can grow much more in the future:
engine based on `SDL2` and `EnTT`.
* [Ducktape](https://github.com/DucktapeEngine/Ducktape): an open source C++
2D & 3D game engine that focuses on being fast and powerful.
* [The Worst Engine](https://github.com/Parasik72/TWE): a game engine based on
OpenGL.
* [Ecsact](https://ecsact.dev/): a language aimed at describing ECS, with a
[runtime implementation](https://github.com/ecsact-dev/ecsact_rt_entt) based
on `EnTT`.
* [AGE (Arc Game Engine)](https://github.com/MohitSethi99/ArcGameEngine): an
open-source engine for building 2D & 3D real-time rendering and interactive
contents.
* [Kengine](https://github.com/phisko/kengine): the _Koala engine_ is a game
engine entirely implemented as an entity-component-ystem.
## Articles, videos and blog posts:
* Articles, videos and blog posts:
* [Some posts](https://skypjack.github.io/tags/#entt) on my personal
[blog](https://skypjack.github.io/) are about `EnTT`, for those who want to
know **more** on this project.
@ -193,6 +227,12 @@ I hope this list can grow much more in the future:
- ... And so on.
[Check out](https://www.youtube.com/channel/UCQ-W1KE9EYfdxhL6S4twUNw) the
_Game Engine Series_ by The Cherno for more videos.
* [Warmonger Dynasty devlog series](https://david-delassus.medium.com/list/warmonger-dynasty-devlogs-f64b71f556de)
by [linkdd](https://github.com/linkdd): an interesting walkthrough of
developing a game (also) with EnTT.
* [Use EnTT When You Need An ECS](https://www.codingwiththomas.com/blog/use-entt-when-you-need-an-ecs)
by [Thomas](https://www.codingwiththomas.com/): I couldn't have said it
better.
* [Space Battle: Huge edition](http://victor.madtriangles.com/code%20experiment/2018/06/11/post-ecs-battle-huge.html):
huge space battle built entirely from scratch.
* [Space Battle](https://github.com/vblanco20-1/ECS_SpaceBattle): huge space
@ -219,7 +259,8 @@ I hope this list can grow much more in the future:
MMO(RPG)s and its [follow-up](https://youtu.be/yGlZeopx2hU) episode about
player bots and full external ECS: a series definitely worth looking at.
* Any Other Business:
## Any Other Business:
* [ArcGIS Runtime SDKs](https://developers.arcgis.com/arcgis-runtime/) by
[Esri](https://www.esri.com/): they use `EnTT` for the internal ECS and the
cross platform C++ rendering engine. The SDKs are utilized by a lot of
@ -261,6 +302,3 @@ I hope this list can grow much more in the future:
* GitHub contains also
[many other examples](https://github.com/search?o=desc&q=%22skypjack%2Fentt%22&s=indexed&type=Code)
of use of `EnTT` from which to take inspiration if interested.
If you know of other resources out there that are about `EnTT`, feel free to
open an issue or a PR and I'll be glad to add them to this page.

View File

@ -67,17 +67,15 @@ recommended.
# Reflection in a nutshell
Reflection always starts from real types (users cannot reflect imaginary types
and it would not make much sense, we wouldn't be talking about reflection
anymore).<br/>
To create a meta node, the library provides the `meta` function that accepts a
type to reflect as a template parameter:
Reflection always starts from actual C++ types. Users cannot reflect _imaginary_
types.<br/>
The `meta` function is where it all starts:
```cpp
auto factory = entt::meta<my_type>();
```
The returned value is a factory object to use to continue building the meta
The returned value is a _factory object_ to use to continue building the meta
type.
By default, a meta type is associated with the identifier returned by the
@ -88,45 +86,42 @@ However, it's also possible to assign custom identifiers to meta types:
auto factory = entt::meta<my_type>().type("reflected_type"_hs);
```
Identifiers are important because users can retrieve meta types at runtime by
searching for them by _name_ other than by type.<br/>
On the other hand, there are cases in which users can be interested in adding
features to a reflected type so that the reflection system can use it correctly
under the hood, but they don't want to also make the type _searchable_. In this
case, it's sufficient not to invoke `type`.
Identifiers are used to _retrieve_ meta types at runtime by _name_ other than by
type.<br/>
However, users can be interested in adding features to a reflected type so that
the reflection system can use it correctly under the hood, while they don't want
to also make the type _searchable_. In this case, it's sufficient not to invoke
`type`.
A factory is such that all its member functions return the factory itself or a
decorated version of it. This object can be used to add the following:
A factory is such that all its member functions return the factory itself. It's
generally used to create the following:
* _Constructors_. Actual constructors can be assigned to a reflected type by
specifying their list of arguments. Free functions (namely, factories) can be
used as well, as long as the return type is the expected one. From a client's
point of view, nothing changes if a constructor is a free function or an
actual constructor.<br/>
Use the `ctor` member function for this purpose:
* _Constructors_. A constructors is assigned to a reflected type by specifying
its _list of arguments_. Free functions are also accepted if the return type
is the expected one. From a client perspective, nothing changes between a free
function or an actual constructor:
```cpp
entt::meta<my_type>().ctor<int, char>().ctor<&factory>();
```
* _Destructors_. Free functions and member functions can be used as destructors
of reflected types. The purpose is to give users the ability to free up
resources that require special treatment before an object is actually
destroyed.<br/>
Use the `dtor` member function for this purpose:
Meta default constructors are implicitly generated, if possible.
* _Destructors_. Both free functions and member functions are valid destructors:
```cpp
entt::meta<my_type>().dtor<&destroy>();
```
The purpose is to offer the possibility to free up resources that require
_special treatment_ before an object is actually destroyed.<br/>
A function should neither delete nor explicitly invoke the destructor of a
given instance.
* _Data members_. Both real data members of the underlying type and static and
global variables, as well as constants of any kind, can be attached to a meta
type. From the point of view of the client, all the variables associated with
the reflected type will appear as if they were part of the type itself.<br/>
Use the `data` member function for this purpose:
* _Data members_. Meta data members are actual data members of the underlying
type but also static and global variables or constants of any kind. From the
point of view of the client, all the variables associated with the reflected
type appear as if they were part of the type itself:
```cpp
entt::meta<my_type>()
@ -135,13 +130,11 @@ decorated version of it. This object can be used to add the following:
.data<&global_variable>("global"_hs);
```
The function requires as an argument the identifier to give to the meta data
once created. Users can then access meta data at runtime by searching for them
by _name_.<br/>
Data members can also be defined by means of a setter and getter pair. Setters
and getters can be either free functions, class members or a mix of them, as
long as they respect the required signatures. This approach is also convenient
to create a read-only variable from a non-const data member:
The `data` function requires the identifier to use for the meta data member.
Users can then access it by _name_ at runtime.<br/>
Data members are also defined by means of a setter and getter pair. These are
either free functions, class members or a mix of them. This approach is also
convenient to create read-only properties from a non-const data member:
```cpp
entt::meta<my_type>().data<nullptr, &my_type::data_member>("member"_hs);
@ -153,13 +146,10 @@ decorated version of it. This object can be used to add the following:
entt::meta<my_type>().data<entt::value_list<&from_int, &from_string>, &my_type::data_member>("member"_hs);
```
Refer to the inline documentation for all the details.
* _Member functions_. Both real member functions of the underlying type and free
functions can be attached to a meta type. From the point of view of the
client, all the functions associated with the reflected type will appear as if
they were part of the type itself.<br/>
Use the `func` member function for this purpose:
* _Member functions_. Meta member functions are actual member functions of the
underlying type but also plain free functions. From the point of view of the
client, all the functions associated with the reflected type appear as if they
were part of the type itself:
```cpp
entt::meta<my_type>()
@ -168,40 +158,31 @@ decorated version of it. This object can be used to add the following:
.func<&free_function>("free"_hs);
```
The function requires as an argument the identifier to give to the meta
function once created. Users can then access meta functions at runtime by
searching for them by _name_.<br/>
The `func` function requires the identifier to use for the meta data function.
Users can then access it by _name_ at runtime.<br/>
Overloading of meta functions is supported. Overloaded functions are resolved
at runtime by the reflection system according to the types of the arguments.
* _Base classes_. A base class is such that the underlying type is actually
derived from it. In this case, the reflection system tracks the relationship
and allows for implicit casts at runtime when required.<br/>
Use the `base` member function for this purpose:
derived from it:
```cpp
entt::meta<derived_type>().base<base_type>();
```
From now on, wherever a `base_type` is required, an instance of `derived_type`
will also be accepted.
The reflection system tracks the relationship and allows for implicit casts at
runtime when required. In other terms, wherever a `base_type` is required, an
instance of `derived_type` is also accepted.
* _Conversion functions_. Actual types can be converted, this is a fact. Just
think of the relationship between a `double` and an `int` to see it. Similar
to bases, conversion functions allow users to define conversions that will be
implicitly performed by the reflection system when required.<br/>
Use the `conv` member function for this purpose:
* _Conversion functions_. Conversion functions allow users to define conversions
that are implicitly performed by the reflection system when required:
```cpp
entt::meta<double>().conv<int>();
```
That's all, everything users need to create meta types and enjoy the reflection
system. At first glance it may not seem that much, but users usually learn to
appreciate it over time.<br/>
Also, do not forget what these few lines hide under the hood: a built-in,
non-intrusive and macro-free system for reflection in C++. Features that are
definitely worth the price, at least for me.
This is everything users need to create meta types. Refer to the inline
documentation for further details.
## Any to the rescue
@ -214,13 +195,13 @@ The API is very similar to that of the `any` type. The class `meta_any` _wraps_
many of the feature to infer a meta node, before forwarding some or all of the
arguments to the underlying storage.<br/>
Among the few relevant differences, `meta_any` adds support for containers and
pointer-like types (see the following sections for more details), while `any`
does not.<br/>
Similar to `any`, this class can also be used to create _aliases_ for unmanaged
pointer-like types, while `any` doesn't.<br/>
Similar to `any`, this class is also used to create _aliases_ for unmanaged
objects either with `forward_as_meta` or using the `std::in_place_type<T &>`
disambiguation tag, as well as from an existing object by means of the `as_ref`
member function. However, unlike `any`, `meta_any` treats an empty instance and
one initialized with `void` differently:
member function.<br/>
Unlike `any` instead, `meta_any` treats an empty instance and one initialized
with `void` differently:
```cpp
entt::meta_any empty{};
@ -229,21 +210,19 @@ entt::meta_any other{std::in_place_type<void>};
While `any` considers both as empty, `meta_any` treats objects initialized with
`void` as if they were _valid_ ones. This allows to differentiate between failed
function calls and function calls that are successful but return nothing.<br/>
function calls and function calls that are successful but return nothing.
Finally, the member functions `try_cast`, `cast` and `allow_cast` are used to
cast the underlying object to a given type (either a reference or a value type)
or to _convert_ a `meta_any` in such a way that a cast becomes viable for the
resulting object. There is in fact no `any_cast` equivalent for `meta_any`.
resulting object.<br/>
There is in fact no `any_cast` equivalent for `meta_any`.
## Enjoy the runtime
Once the web of reflected types has been constructed, it's a matter of using it
at runtime where required.<br/>
All this has the great merit that the reflection system stands in fact as a
non-intrusive tool for the runtime, unlike the vast majority of the things
offered by this library and closely linked to the compile-time.
To search for a reflected type there are a few options:
Once the web of reflected types is constructed, it's a matter of using it at
runtime where required.<br/>
There are a few options to search for a reflected type:
```cpp
// direct access to a reflected type
@ -257,8 +236,8 @@ auto by_type_id = entt::resolve(entt::type_id<my_type>());
```
There exists also an overload of the `resolve` function to use to iterate all
the reflected types at once. It returns an iterable object that can be used in a
range-for loop:
reflected types at once. It returns an iterable object to be used in a range-for
loop:
```cpp
for(auto &&[id, type]: entt::resolve()) {
@ -270,9 +249,7 @@ In all cases, the returned value is an instance of `meta_type` (possibly with
its id). This kind of objects offer an API to know their _runtime identifiers_,
to iterate all the meta objects associated with them and even to build instances
of the underlying type.<br/>
Refer to the inline documentation for all the details.
Meta data members and functions are accessed by name among the other things:
Meta data members and functions are accessed by name:
* Meta data members:
@ -297,11 +274,11 @@ Meta data members and functions are accessed by name among the other things:
A meta function object offers an API to query the underlying type (for
example, to know if it's a const or a static function), to know the number of
arguments, the meta return type and the meta types of the parameters. In
addition, a meta function object can be used to invoke the underlying function
and then get the return value in the form of a `meta_any` object.
addition, a meta function object is used to invoke the underlying function and
then get the return value in the form of a `meta_any` object.
All the meta objects thus obtained as well as the meta types can be explicitly
converted to a boolean value to check if they are valid:
All the meta objects thus obtained as well as the meta types explicitly convert
to a boolean value to check for validity:
```cpp
if(auto func = entt::resolve<my_type>().func("member"_hs); func) {
@ -319,26 +296,23 @@ for(auto &&[id, type]: entt::resolve<my_type>().base()) {
}
```
A meta type can also be used to `construct` actual instances of the underlying
Meta type are also used to `construct` actual instances of the underlying
type.<br/>
In particular, the `construct` member function accepts a variable number of
arguments and searches for a match. It then returns a `meta_any` object that may
or may not be initialized, depending on whether a suitable constructor has been
found or not.
or may not be initialized, depending on whether a suitable constructor was found
or not.
There is no object that wraps the destructor of a meta type nor a `destroy`
member function in its API. Destructors are invoked implicitly by `meta_any`
behind the scenes and users have not to deal with them explicitly. Furthermore,
they have no name, cannot be searched and wouldn't have member functions to
expose anyway.<br/>
Similarly, conversion functions aren't directly accessible. They are used
they've no name, cannot be searched and wouldn't have member functions to expose
anyway.<br/>
Similarly, conversion functions aren't directly accessible. They're used
internally by `meta_any` and the meta objects when needed.
Meta types and meta objects in general contain much more than what is said: a
plethora of functions in addition to those listed whose purposes and uses go
unfortunately beyond the scope of this document.<br/>
I invite anyone interested in the subject to look at the code, experiment and
read the inline documentation to get the best out of this powerful tool.
Meta types and meta objects in general contain much more than what was said.
Refer to the inline documentation for further details.
## Container support
@ -349,7 +323,7 @@ meta system in many cases.
To make a container be recognized as such by the meta system, users are required
to provide specializations for either the `meta_sequence_container_traits` class
or the `meta_associative_container_traits` class, according to the actual type
or the `meta_associative_container_traits` class, according to the actual _type_
of the container.<br/>
`EnTT` already exports the specializations for some common classes. In
particular:
@ -386,11 +360,10 @@ if(any.type().is_sequence_container()) {
The method to use to get a proxy object for associative containers is
`as_associative_container` instead.<br/>
It goes without saying that it's not necessary to perform a double check.
Instead, it's sufficient to query the meta type or verify that the proxy object
is valid. In fact, proxies are contextually convertible to bool to know if they
are valid. For example, invalid proxies are returned when the wrapped object
isn't a container.<br/>
It's not necessary to perform a double check actually. Instead, it's enough to
query the meta type or verify that the proxy object is valid. In fact, proxies
are contextually convertible to bool to check for validity. For example, invalid
proxies are returned when the wrapped object isn't a container.<br/>
In all cases, users aren't expected to _reflect_ containers explicitly. It's
sufficient to assign a container for which a specialization of the traits
classes exists to a `meta_any` object to be able to get its proxy object.
@ -402,32 +375,18 @@ to case. In particular:
* The `value_type` member function returns the meta type of the elements.
* The `size` member function returns the number of elements in the container as
an unsigned integer value:
```cpp
const auto size = view.size();
```
an unsigned integer value.
* The `resize` member function allows to resize the wrapped container and
returns true in case of success:
```cpp
const bool ok = view.resize(3u);
```
returns true in case of success.<br/>
For example, it's not possible to resize fixed size containers.
* The `clear` member function allows to clear the wrapped container and returns
true in case of success:
```cpp
const bool ok = view.clear();
```
true in case of success.<br/>
For example, it's not possible to clear fixed size containers.
* The `begin` and `end` member functions return opaque iterators that can be
used to iterate the container directly:
* The `begin` and `end` member functions return opaque iterators that is used to
iterate the container directly:
```cpp
for(entt::meta_any element: view) {
@ -441,7 +400,7 @@ to case. In particular:
All meta iterators are input iterators and don't offer an indirection operator
on purpose.
* The `insert` member function can be used to add elements to the container. It
* The `insert` member function is used to add elements to the container. It
accepts a meta iterator and the element to insert:
```cpp
@ -451,15 +410,15 @@ to case. In particular:
```
This function returns a meta iterator pointing to the inserted element and a
boolean value to indicate whether the operation was successful or not. Note
that a call to `insert` may silently fail in case of fixed size containers or
whether the arguments aren't at least convertible to the required types.<br/>
Since the meta iterators are contextually convertible to bool, users can rely
on them to know if the operation has failed on the actual container or
upstream, for example for an argument conversion problem.
boolean value to indicate whether the operation was successful or not. A call
to `insert` may silently fail in case of fixed size containers or whether the
arguments aren't at least convertible to the required types.<br/>
Since meta iterators are contextually convertible to bool, users can rely on
them to know if the operation failed on the actual container or upstream, for
example due to an argument conversion problem.
* The `erase` member function can be used to remove elements from the container.
It accepts a meta iterator to the element to remove:
* The `erase` member function is used to remove elements from the container. It
accepts a meta iterator to the element to remove:
```cpp
auto first = view.begin();
@ -468,11 +427,11 @@ to case. In particular:
```
This function returns a meta iterator following the last removed element and a
boolean value to indicate whether the operation was successful or not. Note
that a call to `erase` may silently fail in case of fixed size containers.
boolean value to indicate whether the operation was successful or not. A call
to `erase` may silently fail in case of fixed size containers.
* The `operator[]` can be used to access elements in a container. It accepts a
single argument, that is the position of the element to return:
* The `operator[]` is used to access container elements. It accepts a single
argument, the position of the element to return:
```cpp
for(std::size_t pos{}, last = view.size(); pos < last; ++pos) {
@ -482,8 +441,8 @@ to case. In particular:
```
The function returns instances of `meta_any` that directly refer to the actual
elements. Modifying the returned object will then directly modify the element
inside the container.<br/>
elements. Modifying the returned object directly modifies the element inside
the container.<br/>
Depending on the underlying sequence container, this operation may not be as
efficient. For example, in the case of an `std::list`, a positional access
translates to a linear visit of the list itself (probably not what the user
@ -508,21 +467,13 @@ differences in behavior in the case of key-only containers. In particular:
`std::map<int, char>`.
* The `size` member function returns the number of elements in the container as
an unsigned integer value:
```cpp
const auto size = view.size();
```
an unsigned integer value.
* The `clear` member function allows to clear the wrapped container and returns
true in case of success:
true in case of success.
```cpp
const bool ok = view.clear();
```
* The `begin` and `end` member functions return opaque iterators that can be
used to iterate the container directly:
* The `begin` and `end` member functions return opaque iterators that are used
to iterate the container directly:
```cpp
for(std::pair<entt::meta_any, entt::meta_any> element: view) {
@ -539,11 +490,11 @@ differences in behavior in the case of key-only containers. In particular:
While the accessed key is usually constant in the associative containers and
is therefore returned by copy, the value (if any) is wrapped by an instance of
`meta_any` that directly refers to the actual element. Modifying it will then
directly modify the element inside the container.
`meta_any` that directly refers to the actual element. Modifying it directly
modifies the element inside the container.
* The `insert` member function can be used to add elements to the container. It
accepts two arguments, respectively the key and the value to be inserted:
* The `insert` member function is used to add elements to a container. It gets
two arguments, respectively the key and the value to insert:
```cpp
auto last = view.end();
@ -552,39 +503,39 @@ differences in behavior in the case of key-only containers. In particular:
```
This function returns a boolean value to indicate whether the operation was
successful or not. Note that a call to `insert` may fail when the arguments
aren't at least convertible to the required types.
successful or not. A call to `insert` may fail when the arguments aren't at
least convertible to the required types.
* The `erase` member function can be used to remove elements from the container.
It accepts a single argument, that is the key to be removed:
* The `erase` member function is used to remove elements from a container. It
gets a single argument, the key to remove:
```cpp
view.erase(42);
```
This function returns a boolean value to indicate whether the operation was
successful or not. Note that a call to `erase` may fail when the argument
isn't at least convertible to the required type.
successful or not. A call to `erase` may fail when the argument isn't at least
convertible to the required type.
* The `operator[]` can be used to access elements in a container. It accepts a
single argument, that is the key of the element to return:
* The `operator[]` is used to access elements in a container. It gets a single
argument, the key of the element to return:
```cpp
entt::meta_any value = view[42];
```
The function returns instances of `meta_any` that directly refer to the actual
elements. Modifying the returned object will then directly modify the element
inside the container.
elements. Modifying the returned object directly modifies the element inside
the container.
Container support is minimal but likely sufficient to satisfy all needs.
## Pointer-like types
As with containers, it's also possible to communicate to the meta system which
types to consider _pointers_. This will allow to dereference instances of
`meta_any`, thus obtaining light _references_ to the pointed objects that are
also correctly associated with their meta types.<br/>
As with containers, it's also possible to _tell_ to the meta system which types
are _pointers_. This makes it possible to dereference instances of `meta_any`,
thus obtaining light _references_ to pointed objects that are also correctly
associated with their meta types.<br/>
To make the meta system recognize a type as _pointer-like_, users can specialize
the `is_meta_pointer_like` class. `EnTT` already exports the specializations for
some common classes. In particular:
@ -614,13 +565,12 @@ if(any.type().is_pointer_like()) {
}
```
Of course, it's not necessary to perform a double check. Instead, it's enough to
query the meta type or verify that the returned object is valid. For example,
invalid instances are returned when the wrapped object isn't a pointer-like
type.<br/>
Note that dereferencing a pointer-like object returns an instance of `meta_any`
which refers to the pointed object and allows users to modify it directly
(unless the returned element is const, of course).
It's not necessary to perform a double check. Instead, it's enough to query the
meta type or verify that the returned object is valid. For example, invalid
instances are returned when the wrapped object isn't a pointer-like type.<br/>
Dereferencing a pointer-like object returns an instance of `meta_any` which
_refers_ to the pointed object. Modifying it means modifying the pointed object
directly (unless the returned element is const).
In general, _dereferencing_ a pointer-like type boils down to a `*ptr`. However,
`EnTT` also supports classes that don't offer an `operator*`. In particular:
@ -648,12 +598,12 @@ In general, _dereferencing_ a pointer-like type boils down to a `*ptr`. However,
};
```
In all other cases, that is, when dereferencing a pointer works as expected and
regardless of the pointed type, no user intervention is required.
In all other cases and when dereferencing a pointer works as expected regardless
of the pointed type, no user intervention is required.
## Template information
Meta types also provide a minimal set of information about the nature of the
Meta types also provide a minimal set of information about the _nature_ of the
original type in case it's a class template.<br/>
By default, this works out of the box and requires no user action. However, it's
important to include the header file `template.hpp` to make this information
@ -688,9 +638,9 @@ template<typename Ret, typename... Args>
struct function_type<Ret(Args...)> {};
```
In this case, rather than the function type, the user might want the return type
and unpacked arguments as if they were different template parameters for the
original class template.<br/>
In this case, rather than the function type, it might be useful to provide the
return type and unpacked arguments as if they were different template parameters
for the original class template.<br/>
To achieve this, users must enter the library internals and provide their own
specialization for the class template `entt::meta_template_traits`, such as:
@ -704,8 +654,8 @@ struct entt::meta_template_traits<function_type<Ret(Args...)>> {
The reflection system doesn't verify the accuracy of the information nor infer a
correspondence between real types and meta types.<br/>
Therefore, the specialization will be used as is and the information it contains
will be associated with the appropriate type when required.
Therefore, the specialization is used as is and the information it contains is
associated with the appropriate type when required.
## Automatic conversions
@ -752,29 +702,29 @@ any.allow_cast(type);
int value = any.cast<int>();
```
This should make working with arithmetic types and scoped or unscoped enums as
easy as it is in C++.<br/>
It's also worth noting that it's still possible to set up conversion functions
manually and these will always be preferred over the automatic ones.
This makes working with arithmetic types and scoped or unscoped enums as easy as
it is in C++.<br/>
It's still possible to set up conversion functions manually and these are always
preferred over the automatic ones.
## Implicitly generated default constructor
In many cases, it's useful to be able to create objects of default constructible
types through the reflection system, while not having to explicitly register the
meta type or the default constructor.<br/>
Creating objects of default constructible types through the reflection system
while not having to explicitly register the meta type or its default constructor
is also possible.<br/>
For example, in the case of primitive types like `int` or `char`, but not just
them.
For this reason and only for default constructible types, default constructors
are automatically defined and associated with their meta types, whether they are
explicitly or implicitly generated.<br/>
For default constructible types only, default constructors are automatically
defined and associated with their meta types, whether they are explicitly or
implicitly generated.<br/>
Therefore, this is all is needed to construct an integer from its meta type:
```cpp
entt::resolve<int>().construct();
```
Where the meta type can be for example the one returned from a meta container,
Where the meta type is for example the one returned from a meta container,
useful for building keys without knowing or having to register the actual types.
In all cases, when users register default constructors, they are preferred both
@ -783,8 +733,8 @@ during searches and when the `construct` member function is invoked.
## From void to any
Sometimes all a user has is an opaque pointer to an object of a known meta type.
It would be handy in this case to be able to construct a `meta_any` object from
them.<br/>
It would be handy in this case to be able to construct a `meta_any` element from
it.<br/>
For this purpose, the `meta_type` class offers a `from_void` member function
designed to convert an opaque pointer into a `meta_any`:
@ -792,9 +742,8 @@ designed to convert an opaque pointer into a `meta_any`:
entt::meta_any any = entt::resolve(id).from_void(element);
```
It goes without saying that it's not possible to do a check on the actual type.
Therefore, this call can be considered as a _static cast_ with all the problems
and undefined behaviors of the case following errors.<br/>
Unfortunately, it's not possible to do a check on the actual type. Therefore,
this call can be considered as a _static cast_ with all its _problems_.<br/>
On the other hand, the ability to construct a `meta_any` from an opaque pointer
opens the door to some pretty interesting uses that are worth exploring.
@ -826,17 +775,17 @@ There are a few alternatives available at the moment:
entt::meta<my_type>().func<&my_type::member_function, entt::as_void_t>("member"_hs);
```
If the use with functions is obvious, it must be said that it's also possible
to use this policy with constructors and data members. In the first case, the
constructor will be invoked but the returned wrapper will actually be empty.
In the second case, instead, the property will not be accessible for reading.
If the use with functions is obvious, perhaps less so is use with constructors
and data members. In the first case, the returned wrapper is always empty even
though the constructor is still invoked. In the second case, the property
isn't accessible for reading instead.
* The _as-ref_ and _as-cref_ policies, associated with the types
`entt::as_ref_t` and `entt::as_cref_t`.<br/>
They allow to build wrappers that act as references to unmanaged objects.
Accessing the object contained in the wrapper for which the _reference_ was
requested will make it possible to directly access the instance used to
initialize the wrapper itself:
requested makes it possible to directly access the instance used to initialize
the wrapper itself:
```cpp
entt::meta<my_type>().data<&my_type::data_member, entt::as_ref_t>("member"_hs);
@ -854,21 +803,16 @@ obvious corner cases that can in turn be solved with the use of policies.
## Named constants and enums
A special mention should be made for constant values and enums. It wouldn't be
necessary, but it will help distracted readers.
As mentioned, the `data` member function can be used to reflect constants of any
type among the other things.<br/>
This allows users to create meta types for enums that will work exactly like any
other meta type built from a class. Similarly, arithmetic types can be enriched
As mentioned, the `data` member function is used to reflect constants of any
type.<br/>
This allows users to create meta types for enums that work exactly like any
other meta type built from a class. Similarly, arithmetic types are _enriched_
with constants of special meaning where required.<br/>
Personally, I find it very useful not to export what is the difference between
enums and classes in C++ directly in the space of the reflected types.
All values thus exported appear to users as if they were constant data members
of the reflected types. This avoids the need to _export_ what is the difference
between enums and classes in C++ directly in the space of the reflected types.
All the values thus exported will appear to users as if they were constant data
members of the reflected types.
Exporting constant values or elements from an enum is as simple as ever:
Exposing constant values or elements from an enum is quite simple:
```cpp
entt::meta<my_enum>()
@ -878,28 +822,22 @@ entt::meta<my_enum>()
entt::meta<int>().data<2048>("max_int"_hs);
```
It goes without saying that accessing them is trivial as well. It's a matter of
doing the following, as with any other data member of a meta type:
Accessing them is trivial as well. It's a matter of doing the following, as with
any other data member of a meta type:
```cpp
auto value = entt::resolve<my_enum>().data("a_value"_hs).get({}).cast<my_enum>();
auto max = entt::resolve<int>().data("max_int"_hs).get({}).cast<int>();
```
As a side note, remember that all this happens behind the scenes without any
allocation because of the small object optimization performed by the `meta_any`
class.
All this happens behind the scenes without any allocation because of the small
object optimization performed by the `meta_any` class.
## Properties and meta objects
Sometimes (for example, when it comes to creating an editor) it might be useful
to attach properties to the meta objects created. Fortunately, this is possible
for most of them.<br/>
For the meta objects that support properties, the member functions of the
factory used for registering them will return an extended version of the factory
itself. The latter can be used to attach properties to the last created meta
object.<br/>
Apparently, it's more difficult to say than to do:
for most of them:
```cpp
entt::meta<my_type>().type("reflected_type"_hs).prop("tooltip"_hs, "message");
@ -914,10 +852,10 @@ Key only properties are also supported out of the box:
entt::meta<my_type>().type("reflected_type"_hs).prop(my_enum::key_only);
```
To attach multiple properties to a meta object, it's possible to invoke `prop`
more than once.<br/>
It's also possible to invoke `prop` at different times, as long as the factory
is reset to the meta object of interest.
To attach multiple properties to a meta object, just invoke `prop` more than
once.<br/>
It's also possible to call `prop` at different times, as long as the factory is
reset to the meta object of interest.
The meta objects for which properties are supported are currently meta types,
meta data and meta functions.<br/>
@ -940,7 +878,7 @@ form of a `meta_any` object.
## Unregister types
A type registered with the reflection system can also be unregistered. This
A type registered with the reflection system can also be _unregistered_. This
means unregistering all its data members, member functions, conversion functions
and so on. However, base classes aren't unregistered as well, since they don't
necessarily depend on it.<br/>
@ -969,7 +907,7 @@ A type can be re-registered later with a completely different name and form.
## Meta context
All meta types and their parts are created at runtime and stored in a default
_context_. This can be reached via a service locator as:
_context_. This is obtained via a service locator as:
```cpp
auto &&context = entt::locator<entt::meta_context>::value_or();
@ -984,8 +922,8 @@ auto &&context = entt::locator<entt::meta_context>::value_or();
std::swap(context, other);
```
This can be useful for testing purposes or to define multiple contexts with
different meta objects to be used as appropriate.
This is useful for testing purposes or to define multiple context objects with
different meta type to use as appropriate.
If _replacing_ the default context isn't enough, `EnTT` also offers the ability
to use multiple and externally managed contexts with the runtime reflection
@ -998,16 +936,16 @@ entt::meta_ctx context{};
auto factory = entt::meta<my_type>(context).type("reflected_type"_hs);
```
By doing so, the new meta type won't be available in the default context but
will be usable by passing around the new context when needed, such as when
creating a new `meta_any` object:
By doing so, the new meta type isn't available in the default context but is
usable by passing around the new context when needed, such as when creating a
new `meta_any` object:
```cpp
entt::meta_any any{context, std::in_place_type<my_type>};
```
Similarly, to search for meta types in a context other than the default one, it
will be necessary to pass it to the `resolve` function:
Similarly, to search for meta types in a context other than the default one,
it's necessary to pass it to the `resolve` function:
```cpp
entt::meta_type type = entt::resolve(context, "reflected_type"_hs)

View File

@ -26,17 +26,16 @@ This module aims to make it simple and easy to use.
The library allows to define _concepts_ as interfaces to fulfill with concrete
classes without having to inherit from a common base.<br/>
This is, among others, one of the advantages of static polymorphism in general
Among others, this is one of the advantages of static polymorphism in general
and of a generic wrapper like that offered by the `poly` class template in
particular.<br/>
What users get is an object that can be passed around as such and not through a
reference or a pointer, as happens when it comes to working with dynamic
polymorphism.
The result is an object to pass around as such and not through a reference or a
pointer, as it happens when it comes to working with dynamic polymorphism.
Since the `poly` class template makes use of `entt::any` internally, it also
supports most of its feature. Among the most important, the possibility to
create aliases to existing and thus unmanaged objects. This allows users to
exploit the static polymorphism while maintaining ownership of objects.<br/>
supports most of its feature. For example, the possibility to create aliases to
existing and thus unmanaged objects. This allows users to exploit the static
polymorphism while maintaining ownership of objects.<br/>
Likewise, the `poly` class template also benefits from the small buffer
optimization offered by the `entt::any` class and therefore minimizes the number
of allocations, avoiding them altogether where possible.
@ -44,7 +43,7 @@ of allocations, avoiding them altogether where possible.
## Other libraries
There are some very interesting libraries regarding static polymorphism.<br/>
Among all, the two that I prefer are:
The ones that I like more are:
* [`dyno`](https://github.com/ldionne/dyno): runtime polymorphism done right.
* [`Poly`](https://github.com/facebook/folly/blob/master/folly/docs/Poly.md):
@ -69,18 +68,18 @@ use the terminology introduced by Eric Niebler) is to define a _concept_ that
types will have to adhere to.<br/>
For this purpose, the library offers a single class that supports both deduced
and fully defined interfaces. Although having interfaces deduced automatically
is convenient and allows users to write less code in most cases, this has some
is convenient and allows users to write less code in most cases, it has some
limitations and it's therefore useful to be able to get around the deduction by
providing a custom definition for the static virtual table.
Once the interface is defined, it will be sufficient to provide a generic
implementation to fulfill the concept.<br/>
Once the interface is defined, a generic implementation is needed to fulfill the
concept itself.<br/>
Also in this case, the library allows customizations based on types or families
of types, so as to be able to go beyond the generic case where necessary.
## Deduced interface
This is how a concept with a deduced interface is introduced:
This is how a concept with a deduced interface is defined:
```cpp
struct Drawable: entt::type_list<> {
@ -108,12 +107,12 @@ struct Drawable: entt::type_list<> {
};
```
In this case, all parameters must be passed to `invoke` after the reference to
In this case, all parameters are passed to `invoke` after the reference to
`this` and the return value is whatever the internal call returns.<br/>
As for `invoke`, this is a name that is injected into the _concept_ through
`Base`, from which one must necessarily inherit. Since it's also a dependent
name, the `this-> template` form is unfortunately necessary due to the rules of
the language. However, there exists also an alternative that goes through an
the language. However, there also exists an alternative that goes through an
external call:
```cpp
@ -165,12 +164,12 @@ struct Drawable: entt::type_list<bool(int) const> {
Why should a user fully define a concept if the function types are the same as
the deduced ones?<br>
Because, in fact, this is exactly the limitation that can be worked around by
manually defining the static virtual table.
In fact, this is the limitation that can be worked around by manually defining
the static virtual table.
When things are deduced, there is an implicit constraint.<br/>
If the concept exposes a member function called `draw` with function type
`void()`, a concept can be satisfied:
`void()`, a concept is satisfied:
* Either by a class that exposes a member function with the same name and the
same signature.
@ -179,7 +178,7 @@ If the concept exposes a member function called `draw` with function type
interface itself.
In other words, it's not possible to make use of functions not belonging to the
interface, even if they are present in the types that fulfill the concept.<br/>
interface, even if they're part of the types that fulfill the concept.<br/>
Similarly, it's not possible to deduce a function in the static virtual table
with a function type different from that of the associated member function in
the interface itself.
@ -200,8 +199,8 @@ struct Drawable: entt::type_list<> {
};
```
In this case, it's stated that the `draw` method of a generic type will be
enough to satisfy the requirements of the `Drawable` concept.<br/>
In this case, it's stated that the `draw` method of a generic type is enough to
satisfy the requirements of the `Drawable` concept.<br/>
Both member functions and free functions are supported to fulfill concepts:
```cpp
@ -251,15 +250,15 @@ struct DrawableAndErasable: entt::type_list<> {
```
The static virtual table is empty and must remain so.<br/>
On the other hand, `type` no longer inherits from `Base` and instead forwards
On the other hand, `type` no longer inherits from `Base`. Instead, it forwards
its template parameter to the type exposed by the _base class_. Internally, the
size of the static virtual table of the base class is used as an offset for the
local indexes.<br/>
_size_ of the static virtual table of the base class is used as an offset for
the local indexes.<br/>
Finally, by means of the `value_list_cat_t` utility, the implementation consists
in appending the new functions to the previous list.
As for a defined concept instead, also the list of types must be extended, in a
similar way to what is shown for the implementation of the above concept.<br/>
As for a defined concept instead, the list of types is _extended_ in a similar
way to what is shown for the implementation of the above concept.<br/>
To do this, it's useful to declare a function that allows to convert a _concept_
into its underlying `type_list` object:
@ -268,8 +267,8 @@ template<typename... Type>
entt::type_list<Type...> as_type_list(const entt::type_list<Type...> &);
```
The definition isn't strictly required, since the function will only be used
through a `decltype` as it follows:
The definition isn't strictly required, since the function is only used through
a `decltype` as it follows:
```cpp
struct DrawableAndErasable: entt::type_list_cat_t<
@ -286,9 +285,8 @@ Everything else is the same as already shown instead.
# Static polymorphism in the wild
Once the _concept_ and implementation have been introduced, it will be possible
to use the `poly` class template to contain instances that meet the
requirements:
Once the _concept_ and implementation are defined, it's possible to use the
`poly` class template to _wrap_ instances that meet the requirements:
```cpp
using drawable = entt::poly<Drawable>;
@ -310,9 +308,9 @@ instance = square{};
instance->draw();
```
The `poly` class template offers a wide range of constructors, from the default
one (which will return an uninitialized `poly` object) to the copy and move
constructors, as well as the ability to create objects in-place.<br/>
This class offers a wide range of constructors, from the default one (which
returns an uninitialized `poly` object) to the copy and move constructors, as
well as the ability to create objects in-place.<br/>
Among others, there is also a constructor that allows users to wrap unmanaged
objects in a `poly` instance (either const or non-const ones):
@ -329,14 +327,14 @@ drawable other = instance.as_ref();
```
In both cases, although the interface of the `poly` object doesn't change, it
won't construct any element or take care of destroying the referenced objects.
doesn't construct any element or take care of destroying the referenced objects.
Note also how the underlying concept is accessed via a call to `operator->` and
not directly as `instance.draw()`.<br/>
This allows users to decouple the API of the wrapper from that of the concept.
Therefore, where `instance.data()` will invoke the `data` member function of the
poly object, `instance->data()` will map directly to the functionality exposed
by the underlying concept.
Therefore, where `instance.data()` invokes the `data` member function of the
poly object, `instance->data()` maps directly to the functionality exposed by
the underlying concept.
# Storage size and alignment requirement
@ -351,9 +349,9 @@ entt::basic_poly<Drawable, sizeof(double[4]), alignof(double[4])>
The default size is `sizeof(double[2])`, which seems like a good compromise
between a buffer that is too large and one unable to hold anything larger than
an integer. The alignment requirement is optional instead and by default such
that it's the most stringent (the largest) for any object whose size is at most
equal to the one provided.<br/>
an integer. The alignment requirement is optional and by default such that it's
the most stringent (the largest) for any object whose size is at most equal to
the one provided.<br/>
It's worth noting that providing a size of 0 (which is an accepted value in all
respects) will force the system to dynamically allocate the contained objects in
all cases.

View File

@ -15,18 +15,17 @@
# Introduction
Sometimes processes are a useful tool to work around the strict definition of a
system and introduce logic in a different way, usually without resorting to the
introduction of other components.
`EnTT` offers a minimal support to this paradigm by introducing a few classes
that users can use to define and execute cooperative processes.
Processes are a useful tool to work around the strict definition of a system and
introduce logic in a different way, usually without resorting to other component
types.<br/>
`EnTT` offers minimal support to this paradigm by introducing a few classes used
to define and execute cooperative processes.
# The process
A typical process must inherit from the `process` class template that stays true
to the CRTP idiom. Moreover, derived classes must specify what's the intended
type for elapsed times.
A typical task inherits from the `process` class template that stays true to the
CRTP idiom. Moreover, derived classes specify what the intended type for elapsed
times is.
A process should expose publicly the following member functions whether needed
(note that it isn't required to define a function unless the derived class wants
@ -34,39 +33,38 @@ to _override_ the default behavior):
* `void update(Delta, void *);`
It's invoked once per tick until a process is explicitly aborted or it
terminates either with or without errors. Even though it's not mandatory to
declare this member function, as a rule of thumb each process should at
least define it to work properly. The `void *` parameter is an opaque pointer
to user data (if any) forwarded directly to the process during an update.
This is invoked once per tick until a process is explicitly aborted or ends
either with or without errors. Even though it's not mandatory to declare this
member function, as a rule of thumb each process should at least define it to
work _properly_. The `void *` parameter is an opaque pointer to user data (if
any) forwarded directly to the process during an update.
* `void init();`
It's invoked when the process joins the running queue of a scheduler. This
happens as soon as it's attached to the scheduler if the process is a top
level one, otherwise when it replaces its parent if the process is a
continuation.
This is invoked when the process joins the running queue of a scheduler. It
happens usually as soon as the process is attached to the scheduler if it's a
top level one, otherwise when it replaces its parent if it's a _continuation_.
* `void succeeded();`
It's invoked in case of success, immediately after an update and during the
This is invoked in case of success, immediately after an update and during the
same tick.
* `void failed();`
It's invoked in case of errors, immediately after an update and during the
This is invoked in case of errors, immediately after an update and during the
same tick.
* `void aborted();`
It's invoked only if a process is explicitly aborted. There is no guarantee
that it executes in the same tick, this depends solely on whether the
process is aborted immediately or not.
This is invoked only if a process is explicitly aborted. There is no guarantee
that it executes in the same tick, it depends solely on whether the process is
aborted immediately or not.
Derived classes can also change the internal state of a process by invoking
`succeed` and `fail`, as well as `pause` and `unpause` the process itself. All
these are protected member functions made available to be able to manage the
life cycle of a process from a derived class.
`succeed` and `fail`, as well as `pause` and `unpause` the process itself.<br/>
All these are protected member functions made available to manage the life cycle
of a process from a derived class.
Here is a minimal example for the sake of curiosity:
@ -95,14 +93,14 @@ private:
## Adaptor
Lambdas and functors can't be used directly with a scheduler for they are not
Lambdas and functors can't be used directly with a scheduler because they aren't
properly defined processes with managed life cycles.<br/>
This class helps in filling the gap and turning lambdas and functors into
full-featured processes usable by a scheduler.
The function call operator has a signature similar to the one of the `update`
function of a process but for the fact that it receives two extra arguments to
call whenever a process is terminated with success or with an error:
function of a process but for the fact that it receives two extra callbacks to
invoke whenever a process terminates with success or with an error:
```cpp
void(Delta delta, void *data, auto succeed, auto fail);
@ -127,9 +125,9 @@ A cooperative scheduler runs different processes and helps managing their life
cycles.
Each process is invoked once per tick. If it terminates, it's removed
automatically from the scheduler and it's never invoked again. Otherwise it's
automatically from the scheduler and it's never invoked again. Otherwise, it's
a good candidate to run one more time the next tick.<br/>
A process can also have a child. In this case, the parent process is replaced
A process can also have a _child_. In this case, the parent process is replaced
with its child when it terminates and only if it returns with success. In case
of errors, both the parent process and its child are discarded. This way, it's
easy to create chain of processes to run sequentially.
@ -138,18 +136,25 @@ Using a scheduler is straightforward. To create it, users must provide only the
type for the elapsed times and no arguments at all:
```cpp
entt::scheduler<std::uint32_t> scheduler;
entt::basic_scheduler<std::uint64_t> scheduler;
```
It has member functions to query its internal data structures, like `empty` or
`size`, as well as a `clear` utility to reset it to a clean state:
Otherwise, the `scheduler` alias is also available for the most common cases. It
uses `std::uint32_t` as a default type:
```cpp
entt::scheduler scheduler;
```
The class has member functions to query its internal data structures, like
`empty` or `size`, as well as a `clear` utility to reset it to a clean state:
```cpp
// checks if there are processes still running
const auto empty = scheduler.empty();
// gets the number of processes still running
entt::scheduler<std::uint32_t>::size_type size = scheduler.size();
entt::scheduler::size_type size = scheduler.size();
// resets the scheduler to its initial state and discards all the processes
scheduler.clear();
@ -173,7 +178,7 @@ To attach a process to a scheduler there are mainly two ways:
```
In both cases, the return value is an opaque object that offers a `then` member
function to use to create chains of processes to run sequentially.<br/>
function used to create chains of processes to run sequentially.<br/>
As a minimal example of use:
```cpp
@ -201,7 +206,7 @@ scheduler.update(delta, &data);
```
In addition to these functions, the scheduler offers an `abort` member function
that can be used to discard all the running processes at once:
that is used to discard all the running processes at once:
```cpp
// aborts all the processes abruptly ...

View File

@ -1,18 +1,35 @@
# Similar projects
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Similar projects](#similar-projects)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
There are many projects similar to `EnTT`, both open source and not.<br/>
Some even borrowed some ideas from this library and expressed them in different
languages.<br/>
Others developed different architectures from scratch and therefore offer
alternative solutions with their pros and cons.
Below an incomplete list of those that I've come across so far.<br/>
If you know of other similar projects out there, feel free to open an issue or a
PR and I'll be glad to add them to this page.<br/>
I hope the following lists can grow much more in the future.
# Similar projects
Below an incomplete list of similar projects that I've come across so far.<br/>
If some terms or designs aren't clear, I recommend referring to the
[_ECS Back and Forth_](https://skypjack.github.io/tags/#ecs) series for all the
details.
I hope this list can grow much more in the future:
* C:
* [destral_ecs](https://github.com/roig/destral_ecs): a single-file ECS based
on sparse sets.
@ -34,6 +51,8 @@ I hope this list can grow much more in the future:
solution between an ECS and dynamic mixins.
* C#
* [Arch](https://github.com/genaray/Arch): a simple, fast and _unity entities_
inspired archetype ECS with optional multithreading.
* [Entitas](https://github.com/sschmid/Entitas-CSharp): the ECS framework for
C# and Unity, where _reactive systems_ were invented.
* [LeoECS](https://github.com/Leopotam/ecs): simple lightweight C# Entity
@ -70,6 +89,3 @@ I hope this list can grow much more in the future:
* Zig
* [zig-ecs](https://github.com/prime31/zig-ecs): a _zig-ification_ of `EnTT`.
If you know of other resources out there that can be of interest for the reader,
feel free to open an issue or a PR and I'll be glad to add them to this page.

View File

@ -9,6 +9,7 @@
* [Delegate](#delegate)
* [Runtime arguments](#runtime-arguments)
* [Lambda support](#lambda-support)
* [Raw access](#raw-access)
* [Signals](#signals)
* [Event dispatcher](#event-dispatcher)
* [Named queues](#named-queues)
@ -38,7 +39,7 @@ lightweight classes to solve the same and many other problems.
# Delegate
A delegate can be used as a general purpose invoker with no memory overhead for
free functions and member functions provided along with an instance on which to
free functions, lambdas and members provided along with an instance on which to
invoke them.<br/>
It doesn't claim to be a drop-in replacement for an `std::function`, so don't
expect to use it whenever an `std::function` fits well. That said, it's most
@ -92,9 +93,9 @@ delegate.connect<&g>(c);
delegate(42);
```
The function `g` is invoked with a reference to `c` and `42`. However, the
function type of the delegate is still `void(int)`. This is also the signature
of its function call operator.<br/>
Function `g` is invoked with a reference to `c` and `42`. However, the function
type of the delegate is still `void(int)`. This is also the signature of its
function call operator.<br/>
Another interesting aspect of the delegate class is that it accepts functions
with a list of parameters that is shorter than that of its function type:
@ -105,9 +106,15 @@ delegate(42);
```
Where the function type of the delegate is `void(int)` as above. It goes without
saying that the extra arguments are silently discarded internally.<br/>
This is a nice-to-have feature in a lot of cases, as an example when the
`delegate` class is used as a building block of a signal-slot system.
saying that the extra arguments are silently discarded internally. This is a
nice-to-have feature in a lot of cases, as an example when the `delegate` class
is used as a building block of a signal-slot system.<br/>
In fact, this filtering works both ways. The class tries to pass its first
_count_ arguments **first**, then the last _count_. Watch out for conversion
rules if in doubt when connecting a listener!<br/>
Arbitrary functions that pull random arguments from the delegate list aren't
supported instead. Other feature were preferred, such as support for functions
with compatible argument lists although not equal to those of the delegate.
To create and initialize a delegate at once, there are a few specialized
constructors. Because of the rules of the language, the listener is provided by
@ -231,6 +238,24 @@ As above, the first parameter (`const void *`) isn't part of the function type
of the delegate and is used to dispatch arbitrary user data back and forth. In
other terms, the function type of the delegate above is `int(int)`.
## Raw access
While not recommended, a delegate also allows direct access to the stored
callable function target and underlying data, if any.<br/>
This makes it possible to bypass the behavior of the delegate itself and force
calls on different instances:
```cpp
my_struct other;
delegate.target(&other, 42);
```
It goes without saying that this type of approach is **very** risky, especially
since there is no way of knowing whether the contained function was originally a
member function of some class, a free function or a lambda.<br/>
Another possible (and meaningful) use of this feature is that of identifying a
particular delegate through its descriptive _traits_ instead.
# Signals
Signal handlers work with references to classes, function pointers and pointers
@ -290,7 +315,7 @@ sink.disconnect<&foo>();
sink.disconnect<&listener::bar>(instance);
// disconnect all member functions of an instance, if any
sink.disconnect(instance);
sink.disconnect(&instance);
// discards all listeners at once
sink.disconnect();
@ -300,15 +325,6 @@ As shown above, listeners don't have to strictly follow the signature of the
signal. As long as a listener can be invoked with the given arguments to yield a
result that is convertible to the given return type, everything works just
fine.<br/>
It's also possible to connect a listener before other elements already contained
by the signal. The `before` function returns a `sink` object that is correctly
initialized for the purpose and can be used to connect one or more listeners in
order and in the desired position:
```cpp
sink.before<&foo>().connect<&listener::bar>(instance);
```
In all cases, the `connect` member function returns by default a `connection`
object to be used as an alternative to break a connection by means of its
`release` member function.<br/>
@ -409,7 +425,7 @@ of them at once:
```cpp
dispatcher.sink<an_event>().disconnect<&listener::receive>(listener);
dispatcher.sink<another_event>().disconnect(listener);
dispatcher.sink<another_event>().disconnect(&listener);
```
The `trigger` member function serves the purpose of sending an immediate event

View File

@ -1,62 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="entt::basic_registry&lt;*&gt;">
<Intrinsic Name="pools_size" Expression="pools.packed.first_base::value.size()"/>
<Intrinsic Name="vars_size" Expression="vars.ctx.packed.first_base::value.size()"/>
<Intrinsic Name="to_entity" Expression="*((entity_traits::entity_type *)&amp;entity) &amp; entity_traits::entity_mask">
<Parameter Name="entity" Type="entity_traits::value_type &amp;"/>
<Intrinsic Name="to_entity" Expression="*((traits_type::entity_type *)&amp;entity) &amp; traits_type::entity_mask">
<Parameter Name="entity" Type="traits_type::value_type &amp;"/>
</Intrinsic>
<DisplayString>{{ size={ epool.size() } }}</DisplayString>
<DisplayString>{{ pools={ pools.size() } }}</DisplayString>
<Expand>
<Item IncludeView="simple" Name="[epool]">epool,view(simple)nr</Item>
<Synthetic Name="[epool]" ExcludeView="simple">
<DisplayString>{ epool.size() }</DisplayString>
<Expand>
<CustomListItems>
<Variable Name="pos" InitialValue="0" />
<Variable Name="last" InitialValue="epool.size()"/>
<Loop>
<Break Condition="pos == last"/>
<If Condition="to_entity(epool[pos]) == pos">
<Item Name="[{ pos }]">epool[pos]</Item>
</If>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
</Expand>
</Synthetic>
<Synthetic Name="[destroyed]" ExcludeView="simple">
<DisplayString>{ to_entity(free_list) != entity_traits::entity_mask }</DisplayString>
<Expand>
<CustomListItems>
<Variable Name="it" InitialValue="to_entity(free_list)" />
<Loop>
<Break Condition="it == entity_traits::entity_mask"/>
<Item Name="[{ it }]">epool[it]</Item>
<Exec>it = to_entity(epool[it])</Exec>
</Loop>
</CustomListItems>
</Expand>
</Synthetic>
<Item Name="[entities]">entities</Item>
<Synthetic Name="[pools]">
<DisplayString>{ pools_size() }</DisplayString>
<DisplayString>{ pools.size() }</DisplayString>
<Expand>
<IndexListItems ExcludeView="simple">
<Size>pools_size()</Size>
<Size>pools.size()</Size>
<ValueNode>*pools.packed.first_base::value[$i].element.second</ValueNode>
</IndexListItems>
<IndexListItems IncludeView="simple">
<Size>pools_size()</Size>
<Size>pools.size()</Size>
<ValueNode>*pools.packed.first_base::value[$i].element.second,view(simple)</ValueNode>
</IndexListItems>
</Expand>
</Synthetic>
<Item Name="[groups]" ExcludeView="simple">groups.size()</Item>
<Synthetic Name="[vars]">
<DisplayString>{ vars_size() }</DisplayString>
<DisplayString>{ vars.ctx.size() }</DisplayString>
<Expand>
<IndexListItems>
<Size>vars_size()</Size>
<Size>vars.ctx.size()</Size>
<ValueNode>vars.ctx.packed.first_base::value[$i].element.second</ValueNode>
</IndexListItems>
</Expand>
@ -69,20 +38,20 @@
<Item Name="[capacity]" ExcludeView="simple">packed.capacity()</Item>
<Item Name="[policy]">mode,en</Item>
<Synthetic Name="[sparse]">
<DisplayString>{ sparse.size() * entity_traits::page_size }</DisplayString>
<DisplayString>{ sparse.size() * traits_type::page_size }</DisplayString>
<Expand>
<ExpandedItem IncludeView="simple">sparse,view(simple)</ExpandedItem>
<CustomListItems ExcludeView="simple">
<Variable Name="pos" InitialValue="0"/>
<Variable Name="page" InitialValue="0"/>
<Variable Name="offset" InitialValue="0"/>
<Variable Name="last" InitialValue="sparse.size() * entity_traits::page_size"/>
<Variable Name="last" InitialValue="sparse.size() * traits_type::page_size"/>
<Loop>
<Break Condition="pos == last"/>
<Exec>page = pos / entity_traits::page_size</Exec>
<Exec>offset = pos &amp; (entity_traits::page_size - 1)</Exec>
<If Condition="sparse[page] &amp;&amp; (*((entity_traits::entity_type *)&amp;sparse[page][offset]) &lt; ~entity_traits::entity_mask)">
<Item Name="[{ pos }]">*((entity_traits::entity_type *)&amp;sparse[page][offset]) &amp; entity_traits::entity_mask</Item>
<Exec>page = pos / traits_type::page_size</Exec>
<Exec>offset = pos &amp; (traits_type::page_size - 1)</Exec>
<If Condition="sparse[page] &amp;&amp; (*((traits_type::entity_type *)&amp;sparse[page][offset]) &lt; ~traits_type::entity_mask)">
<Item Name="[{ pos }]">*((traits_type::entity_type *)&amp;sparse[page][offset]) &amp; traits_type::entity_mask</Item>
</If>
<Exec>++pos</Exec>
</Loop>
@ -98,7 +67,7 @@
<Variable Name="last" InitialValue="packed.size()"/>
<Loop>
<Break Condition="pos == last"/>
<If Condition="*((entity_traits::entity_type *)&amp;packed[pos]) &lt; ~entity_traits::entity_mask">
<If Condition="*((traits_type::entity_type *)&amp;packed[pos]) &lt; ~traits_type::entity_mask">
<Item Name="[{ pos }]">packed[pos]</Item>
</If>
<Exec>++pos</Exec>
@ -111,18 +80,19 @@
<Type Name="entt::basic_storage&lt;*&gt;">
<DisplayString>{{ size={ base_type::packed.size() }, type={ base_type::info->alias,na } }}</DisplayString>
<Expand>
<Item Name="[capacity]" Optional="true" ExcludeView="simple">packed.first_base::value.capacity() * comp_traits::page_size</Item>
<Item Name="[page size]" Optional="true" ExcludeView="simple">comp_traits::page_size</Item>
<Item Name="[capacity]" Optional="true" ExcludeView="simple">payload.capacity() * traits_type::page_size</Item>
<Item Name="[page size]" Optional="true" ExcludeView="simple">traits_type::page_size</Item>
<Item Name="[length]" Optional="true" ExcludeView="simple">length</Item>
<Item Name="[base]" ExcludeView="simple">(base_type*)this,nand</Item>
<Item Name="[base]" IncludeView="simple">(base_type*)this,view(simple)nand</Item>
<!-- having SFINAE-like techniques in natvis is priceless :) -->
<CustomListItems Condition="packed.first_base::value.size() != 0" Optional="true">
<CustomListItems Condition="payload.size() != 0" Optional="true">
<Variable Name="pos" InitialValue="0" />
<Variable Name="last" InitialValue="base_type::packed.size()"/>
<Loop>
<Break Condition="pos == last"/>
<If Condition="*((base_type::entity_traits::entity_type *)&amp;base_type::packed[pos]) &lt; ~base_type::entity_traits::entity_mask">
<Item Name="[{ pos }:{ base_type::packed[pos] }]">packed.first_base::value[pos / comp_traits::page_size][pos &amp; (comp_traits::page_size - 1)]</Item>
<If Condition="*((base_type::traits_type::entity_type *)&amp;base_type::packed[pos]) &lt; ~base_type::traits_type::entity_mask">
<Item Name="[{ pos }:{ base_type::packed[pos] }]">payload[pos / traits_type::page_size][pos &amp; (traits_type::page_size - 1)]</Item>
</If>
<Exec>++pos</Exec>
</Loop>

View File

@ -8,7 +8,7 @@
</Expand>
</Type>
<Type Name="entt::basic_dispatcher&lt;*&gt;">
<Intrinsic Name="size" Expression="pools.first_base::value.packed.first_base::value.size()"/>
<Intrinsic Name="size" Expression="pools.first_base::value.size()"/>
<DisplayString>{{ size={ size() } }}</DisplayString>
<Expand>
<Synthetic Name="[pools]">
@ -50,7 +50,6 @@
<DisplayString>{{ type={ "$T1" } }}</DisplayString>
<Expand>
<Item Name="[signal]">signal,na</Item>
<Item Name="[offset]">offset</Item>
</Expand>
</Type>
</AutoVisualizer>

File diff suppressed because it is too large Load Diff

View File

@ -50,6 +50,8 @@
# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg)
#endif
#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg);
#ifdef ENTT_NO_ETO
# define ENTT_ETO_TYPE(Type) void
#else

View File

@ -4,8 +4,8 @@
#include "macro.h"
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 11
#define ENTT_VERSION_PATCH 1
#define ENTT_VERSION_MINOR 12
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \

View File

@ -128,51 +128,51 @@ public:
return {it->element.first, it->element.second};
}
template<typename ILhs, typename IRhs>
friend constexpr std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) noexcept;
template<typename Lhs, typename Rhs>
friend constexpr std::ptrdiff_t operator-(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
template<typename ILhs, typename IRhs>
friend constexpr bool operator==(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) noexcept;
template<typename Lhs, typename Rhs>
friend constexpr bool operator==(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
template<typename ILhs, typename IRhs>
friend constexpr bool operator<(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) noexcept;
template<typename Lhs, typename Rhs>
friend constexpr bool operator<(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
private:
It it;
};
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return lhs.it - rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator==(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator==(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return lhs.it == rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator!=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator!=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator<(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator<(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return lhs.it < rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator>(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator>(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return rhs < lhs;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator<=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator<=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator>=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator>=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
return !(lhs < rhs);
}
@ -230,13 +230,13 @@ private:
std::size_t offset;
};
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator<Lhs> &lhs, const dense_map_local_iterator<Rhs> &rhs) noexcept {
return lhs.index() == rhs.index();
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator<Lhs> &lhs, const dense_map_local_iterator<Rhs> &rhs) noexcept {
return !(lhs == rhs);
}
@ -266,7 +266,7 @@ class dense_map {
static constexpr std::size_t minimum_capacity = 8u;
using node_type = internal::dense_map_node<Key, Type>;
using alloc_traits = typename std::allocator_traits<Allocator>;
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, std::pair<const Key, Type>>, "Invalid value type");
using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
@ -464,7 +464,6 @@ public:
/**
* @brief Returns an iterator to the beginning.
*
* The returned iterator points to the first instance of the internal array.
* If the array is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first instance of the internal array.
@ -485,11 +484,6 @@ public:
/**
* @brief Returns an iterator to the end.
*
* The returned iterator points to the element following the last instance
* of the internal array. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @return An iterator to the element following the last instance of the
* internal array.
*/
@ -838,7 +832,7 @@ public:
}
/*! @copydoc equal_range */
template<class Other>
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<const_iterator, const_iterator>>>
equal_range(const Other &key) const {
const auto it = find(key);

View File

@ -96,51 +96,51 @@ public:
return *operator->();
}
template<typename ILhs, typename IRhs>
friend constexpr std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) noexcept;
template<typename Lhs, typename Rhs>
friend constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
template<typename ILhs, typename IRhs>
friend constexpr bool operator==(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) noexcept;
template<typename Lhs, typename Rhs>
friend constexpr bool operator==(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
template<typename ILhs, typename IRhs>
friend constexpr bool operator<(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) noexcept;
template<typename Lhs, typename Rhs>
friend constexpr bool operator<(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
private:
It it;
};
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return lhs.it - rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator==(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator==(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return lhs.it == rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator!=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator!=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator<(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator<(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return lhs.it < rhs.it;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator>(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator>(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return rhs < lhs;
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator<=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator<=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator>=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator>=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
return !(lhs < rhs);
}
@ -195,13 +195,13 @@ private:
std::size_t offset;
};
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator<Lhs> &lhs, const dense_set_local_iterator<Rhs> &rhs) noexcept {
return lhs.index() == rhs.index();
}
template<typename ILhs, typename IRhs>
[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator<Lhs> &lhs, const dense_set_local_iterator<Rhs> &rhs) noexcept {
return !(lhs == rhs);
}
@ -410,7 +410,6 @@ public:
/**
* @brief Returns an iterator to the beginning.
*
* The returned iterator points to the first instance of the internal array.
* If the array is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first instance of the internal array.
@ -431,11 +430,6 @@ public:
/**
* @brief Returns an iterator to the end.
*
* The returned iterator points to the element following the last instance
* of the internal array. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @return An iterator to the element following the last instance of the
* internal array.
*/
@ -691,7 +685,7 @@ public:
}
/*! @copydoc equal_range */
template<class Other>
template<typename Other>
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<const_iterator, const_iterator>>>
equal_range(const Other &value) const {
const auto it = find(value);

View File

@ -95,14 +95,15 @@ struct radix_sort {
template<typename It, typename Getter = identity>
void operator()(It first, It last, Getter getter = Getter{}) const {
if(first < last) {
static constexpr auto mask = (1 << Bit) - 1;
static constexpr auto buckets = 1 << Bit;
static constexpr auto passes = N / Bit;
constexpr auto passes = N / Bit;
using value_type = typename std::iterator_traits<It>::value_type;
std::vector<value_type> aux(std::distance(first, last));
auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) {
constexpr auto mask = (1 << Bit) - 1;
constexpr auto buckets = 1 << Bit;
std::size_t index[buckets]{};
std::size_t count[buckets]{};

View File

@ -59,7 +59,7 @@ class basic_any {
};
template<typename Type>
static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len &&std::is_nothrow_move_constructible_v<Type>;
static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>;
template<typename Type>
static const void *basic_vtable(const operation op, const basic_any &value, const void *other) {
@ -128,17 +128,17 @@ class basic_any {
vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
if constexpr(std::is_lvalue_reference_v<Type>) {
static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref;
instance = (std::addressof(args), ...);
} else if constexpr(in_situ<std::remove_cv_t<std::remove_reference_t<Type>>>) {
if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
if constexpr(std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>)) {
new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
} else {
new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
}
} else {
if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
if constexpr(std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>)) {
instance = new std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
} else {
instance = new std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
@ -428,7 +428,7 @@ private:
* @return The element converted to the requested type.
*/
template<typename Type, std::size_t Len, std::size_t Align>
Type any_cast(const basic_any<Len, Align> &data) noexcept {
[[nodiscard]] Type any_cast(const basic_any<Len, Align> &data) noexcept {
const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
ENTT_ASSERT(instance, "Invalid instance");
return static_cast<Type>(*instance);
@ -436,7 +436,7 @@ Type any_cast(const basic_any<Len, Align> &data) noexcept {
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
Type any_cast(basic_any<Len, Align> &data) noexcept {
[[nodiscard]] Type any_cast(basic_any<Len, Align> &data) noexcept {
// forces const on non-reference types to make them work also with wrappers for const references
auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
ENTT_ASSERT(instance, "Invalid instance");
@ -445,7 +445,7 @@ Type any_cast(basic_any<Len, Align> &data) noexcept {
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
Type any_cast(basic_any<Len, Align> &&data) noexcept {
[[nodiscard]] Type any_cast(basic_any<Len, Align> &&data) noexcept {
if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
return static_cast<Type>(std::move(*instance));
@ -461,14 +461,14 @@ Type any_cast(basic_any<Len, Align> &&data) noexcept {
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
[[nodiscard]] const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
const auto &info = type_id<std::remove_cv_t<Type>>();
return static_cast<const Type *>(data->data(info));
}
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
Type *any_cast(basic_any<Len, Align> *data) noexcept {
[[nodiscard]] Type *any_cast(basic_any<Len, Align> *data) noexcept {
if constexpr(std::is_const_v<Type>) {
// last attempt to make wrappers for const references return their values
return any_cast<Type>(&std::as_const(*data));
@ -488,7 +488,7 @@ Type *any_cast(basic_any<Len, Align> *data) noexcept {
* @return A properly initialized wrapper for an object of the given type.
*/
template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args>
basic_any<Len, Align> make_any(Args &&...args) {
[[nodiscard]] basic_any<Len, Align> make_any(Args &&...args) {
return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
}
@ -501,7 +501,7 @@ basic_any<Len, Align> make_any(Args &&...args) {
* @return A properly initialized and not necessarily owning wrapper.
*/
template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type>
basic_any<Len, Align> forward_as_any(Type &&value) {
[[nodiscard]] basic_any<Len, Align> forward_as_any(Type &&value) {
return basic_any<Len, Align>{std::in_place_type<Type &&>, std::forward<Type>(value)};
}

View File

@ -67,7 +67,7 @@ struct basic_hashed_string {
template<typename Char>
class basic_hashed_string: internal::basic_hashed_string<Char> {
using base_type = internal::basic_hashed_string<Char>;
using hs_traits = internal::fnv1a_traits<id_type>;
using traits_type = internal::fnv1a_traits<id_type>;
struct const_wrapper {
// non-explicit constructor on purpose
@ -79,10 +79,10 @@ class basic_hashed_string: internal::basic_hashed_string<Char> {
// FowlerNollVo hash function v. 1a - the good
[[nodiscard]] static constexpr auto helper(const Char *str) noexcept {
base_type base{str, 0u, hs_traits::offset};
base_type base{str, 0u, traits_type::offset};
for(; str[base.length]; ++base.length) {
base.hash = (base.hash ^ static_cast<hs_traits::type>(str[base.length])) * hs_traits::prime;
base.hash = (base.hash ^ static_cast<traits_type::type>(str[base.length])) * traits_type::prime;
}
return base;
@ -90,10 +90,10 @@ class basic_hashed_string: internal::basic_hashed_string<Char> {
// FowlerNollVo hash function v. 1a - the good
[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept {
base_type base{str, len, hs_traits::offset};
base_type base{str, len, traits_type::offset};
for(size_type pos{}; pos < len; ++pos) {
base.hash = (base.hash ^ static_cast<hs_traits::type>(str[pos])) * hs_traits::prime;
base.hash = (base.hash ^ static_cast<traits_type::type>(str[pos])) * traits_type::prime;
}
return base;

View File

@ -106,7 +106,7 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma
/**
* @brief Deleter for allocator-aware unique pointers (waiting for C++20).
* @tparam Args Types of arguments to use to construct the object.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Allocator>
struct allocation_deleter: private Allocator {
@ -127,7 +127,7 @@ struct allocation_deleter: private Allocator {
* @param ptr A valid pointer to an object of the given type.
*/
constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v<typename allocator_type::value_type>) {
using alloc_traits = typename std::allocator_traits<Allocator>;
using alloc_traits = std::allocator_traits<Allocator>;
alloc_traits::destroy(*this, to_address(ptr));
alloc_traits::deallocate(*this, ptr, 1u);
}

View File

@ -69,7 +69,7 @@ struct forward_apply: private Func {
* @tparam Args Types of arguments to use to construct the new instance.
* @param args Parameters to use to construct the instance.
*/
template<class... Args>
template<typename... Args>
constexpr forward_apply(Args &&...args) noexcept(std::is_nothrow_constructible_v<Func, Args...>)
: Func{std::forward<Args>(args)...} {}
@ -79,13 +79,13 @@ struct forward_apply: private Func {
* @param args Parameters to forward to the underlying function.
* @return Return value of the underlying function, if any.
*/
template<class Type>
template<typename Type>
constexpr decltype(auto) operator()(Type &&args) noexcept(noexcept(std::apply(std::declval<Func &>(), args))) {
return std::apply(static_cast<Func &>(*this), std::forward<Type>(args));
}
/*! @copydoc operator()() */
template<class Type>
template<typename Type>
constexpr decltype(auto) operator()(Type &&args) const noexcept(noexcept(std::apply(std::declval<const Func &>(), args))) {
return std::apply(static_cast<const Func &>(*this), std::forward<Type>(args));
}

View File

@ -3,6 +3,7 @@
#include <cstddef>
#include <iterator>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
@ -55,7 +56,6 @@ using type_identity_t = typename type_identity<Type>::type;
/**
* @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
* @tparam Type The type of which to return the size.
* @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
*/
template<typename Type, typename = void>
struct size_of: std::integral_constant<std::size_t, 0u> {};
@ -297,7 +297,8 @@ struct type_list_contains;
* @tparam Other Type to look for.
*/
template<typename... Type, typename Other>
struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
struct type_list_contains<type_list<Type...>, Other>
: std::bool_constant<(std::is_same_v<Type, Other> || ...)> {};
/**
* @brief Helper variable template.
@ -385,10 +386,20 @@ struct value_list_element<Index, value_list<Value, Other...>>
*/
template<auto Value, auto... Other>
struct value_list_element<0u, value_list<Value, Other...>> {
/*! @brief Searched type. */
using type = decltype(Value);
/*! @brief Searched value. */
static constexpr auto value = Value;
};
/**
* @brief Helper type.
* @tparam Index Index of the type to return.
* @tparam List Value list to search into.
*/
template<std::size_t Index, typename List>
using value_list_element_t = typename value_list_element<Index, List>::type;
/**
* @brief Helper type.
* @tparam Index Index of the value to return.
@ -397,6 +408,58 @@ struct value_list_element<0u, value_list<Value, Other...>> {
template<std::size_t Index, typename List>
inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
/*! @brief Primary template isn't defined on purpose. */
template<auto, typename>
struct value_list_index;
/**
* @brief Provides compile-time type access to the values of a value list.
* @tparam Value Value to look for and for which to return the index.
* @tparam First First value provided by the value list.
* @tparam Other Other values provided by the value list.
*/
template<auto Value, auto First, auto... Other>
struct value_list_index<Value, value_list<First, Other...>> {
/*! @brief Unsigned integer type. */
using value_type = std::size_t;
/*! @brief Compile-time position of the given value in the sublist. */
static constexpr value_type value = 1u + value_list_index<Value, value_list<Other...>>::value;
};
/**
* @brief Provides compile-time type access to the values of a value list.
* @tparam Value Value to look for and for which to return the index.
* @tparam Other Other values provided by the value list.
*/
template<auto Value, auto... Other>
struct value_list_index<Value, value_list<Value, Other...>> {
static_assert(value_list_index<Value, value_list<Other...>>::value == sizeof...(Other), "Non-unique type");
/*! @brief Unsigned integer type. */
using value_type = std::size_t;
/*! @brief Compile-time position of the given value in the sublist. */
static constexpr value_type value = 0u;
};
/**
* @brief Provides compile-time type access to the values of a value list.
* @tparam Value Value to look for and for which to return the index.
*/
template<auto Value>
struct value_list_index<Value, value_list<>> {
/*! @brief Unsigned integer type. */
using value_type = std::size_t;
/*! @brief Compile-time position of the given type in the sublist. */
static constexpr value_type value = 0u;
};
/**
* @brief Helper variable template.
* @tparam List Value list.
* @tparam Value Value to look for and for which to return the index.
*/
template<auto Value, typename List>
inline constexpr std::size_t value_list_index_v = value_list_index<Value, List>::value;
/**
* @brief Concatenates multiple value lists.
* @tparam Value Values provided by the first value list.
@ -448,6 +511,89 @@ struct value_list_cat<value_list<Value...>> {
template<typename... List>
using value_list_cat_t = typename value_list_cat<List...>::type;
/*! @brief Primary template isn't defined on purpose. */
template<typename>
struct value_list_unique;
/**
* @brief Removes duplicates values from a value list.
* @tparam Value One of the values provided by the given value list.
* @tparam Other The other values provided by the given value list.
*/
template<auto Value, auto... Other>
struct value_list_unique<value_list<Value, Other...>> {
/*! @brief A value list without duplicate types. */
using type = std::conditional_t<
((Value == Other) || ...),
typename value_list_unique<value_list<Other...>>::type,
value_list_cat_t<value_list<Value>, typename value_list_unique<value_list<Other...>>::type>>;
};
/*! @brief Removes duplicates values from a value list. */
template<>
struct value_list_unique<value_list<>> {
/*! @brief A value list without duplicate types. */
using type = value_list<>;
};
/**
* @brief Helper type.
* @tparam Type A value list.
*/
template<typename Type>
using value_list_unique_t = typename value_list_unique<Type>::type;
/**
* @brief Provides the member constant `value` to true if a value list contains
* a given value, false otherwise.
* @tparam List Value list.
* @tparam Value Value to look for.
*/
template<typename List, auto Value>
struct value_list_contains;
/**
* @copybrief value_list_contains
* @tparam Value Values provided by the value list.
* @tparam Other Value to look for.
*/
template<auto... Value, auto Other>
struct value_list_contains<value_list<Value...>, Other>
: std::bool_constant<((Value == Other) || ...)> {};
/**
* @brief Helper variable template.
* @tparam List Value list.
* @tparam Value Value to look for.
*/
template<typename List, auto Value>
inline constexpr bool value_list_contains_v = value_list_contains<List, Value>::value;
/*! @brief Primary template isn't defined on purpose. */
template<typename...>
class value_list_diff;
/**
* @brief Computes the difference between two value lists.
* @tparam Value Values provided by the first value list.
* @tparam Other Values provided by the second value list.
*/
template<auto... Value, auto... Other>
class value_list_diff<value_list<Value...>, value_list<Other...>> {
using v141_toolset_workaround = value_list<Other...>;
public:
/*! @brief A value list that is the difference between the two value lists. */
using type = value_list_cat_t<std::conditional_t<value_list_contains_v<v141_toolset_workaround, Value>, value_list<>, value_list<Value>>...>;
};
/**
* @brief Helper type.
* @tparam List Value lists between which to compute the difference.
*/
template<typename... List>
using value_list_diff_t = typename value_list_diff<List...>::type;
/*! @brief Same as std::is_invocable, but with tuples. */
template<typename, typename>
struct is_applicable: std::false_type {};
@ -568,7 +714,7 @@ inline constexpr bool is_iterator_v = is_iterator<Type>::value;
*/
template<typename Type>
struct is_ebco_eligible
: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
: std::bool_constant<std::is_empty_v<Type> && !std::is_final_v<Type>> {};
/**
* @brief Helper variable template.
@ -659,6 +805,10 @@ template<typename Type>
struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
/*! @copydoc is_equality_comparable */
template<typename Type, auto N>
struct is_equality_comparable<Type[N]>: std::false_type {};
/**
* @brief Helper variable template.
* @tparam Type The type to test.
@ -755,4 +905,16 @@ using nth_argument_t = typename nth_argument<Index, Candidate>::type;
} // namespace entt
template<typename... Type>
struct std::tuple_size<entt::type_list<Type...>>: std::integral_constant<std::size_t, entt::type_list<Type...>::size> {};
template<std::size_t Index, typename... Type>
struct std::tuple_element<Index, entt::type_list<Type...>>: entt::type_list_element<Index, entt::type_list<Type...>> {};
template<auto... Value>
struct std::tuple_size<entt::value_list<Value...>>: std::integral_constant<std::size_t, entt::value_list<Value...>::size> {};
template<std::size_t Index, auto... Value>
struct std::tuple_element<Index, entt::value_list<Value...>>: entt::value_list_element<Index, entt::value_list<Value...>> {};
#endif

View File

@ -17,7 +17,7 @@ struct identity {
* @param value The actual argument.
* @return The submitted value as-is.
*/
template<class Type>
template<typename Type>
[[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept {
return std::forward<Type>(value);
}
@ -50,7 +50,7 @@ template<typename Func>
* @brief Helper type for visitors.
* @tparam Func Types of function objects.
*/
template<class... Func>
template<typename... Func>
struct overloaded: Func... {
using Func::operator()...;
};
@ -59,14 +59,14 @@ struct overloaded: Func... {
* @brief Deduction guide.
* @tparam Func Types of function objects.
*/
template<class... Func>
template<typename... Func>
overloaded(Func...) -> overloaded<Func...>;
/**
* @brief Basic implementation of a y-combinator.
* @tparam Func Type of a potentially recursive function.
*/
template<class Func>
template<typename Func>
struct y_combinator {
/**
* @brief Constructs a y-combinator from a given function.
@ -81,13 +81,13 @@ struct y_combinator {
* @param args Parameters to use to invoke the underlying function.
* @return Return value of the underlying function, if any.
*/
template<class... Args>
template<typename... Args>
constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v<Func, const y_combinator &, Args...>) {
return func(*this, std::forward<Args>(args)...);
}
/*! @copydoc operator()() */
template<class... Args>
template<typename... Args>
constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v<Func, y_combinator &, Args...>) {
return func(*this, std::forward<Args>(args)...);
}

View File

@ -4,6 +4,7 @@
#include <cstddef>
#include <type_traits>
#include "../config/config.h"
#include "fwd.hpp"
namespace entt {
@ -17,6 +18,9 @@ namespace internal {
template<typename Type, typename = void>
struct in_place_delete: std::bool_constant<!(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>)> {};
template<>
struct in_place_delete<void>: std::false_type {};
template<typename Type>
struct in_place_delete<Type, std::enable_if_t<Type::in_place_delete>>
: std::true_type {};
@ -24,6 +28,9 @@ struct in_place_delete<Type, std::enable_if_t<Type::in_place_delete>>
template<typename Type, typename = void>
struct page_size: std::integral_constant<std::size_t, !std::is_empty_v<ENTT_ETO_TYPE(Type)> * ENTT_PACKED_PAGE> {};
template<>
struct page_size<void>: std::integral_constant<std::size_t, 0u> {};
template<typename Type>
struct page_size<Type, std::enable_if_t<std::is_convertible_v<decltype(Type::page_size), std::size_t>>>
: std::integral_constant<std::size_t, Type::page_size> {};
@ -52,13 +59,6 @@ struct component_traits {
static constexpr std::size_t page_size = internal::page_size<Type>::value;
};
/**
* @brief Helper variable template.
* @tparam Type Type of component.
*/
template<class Type>
inline constexpr bool ignore_as_empty_v = (std::is_void_v<Type> || component_traits<Type>::page_size == 0u);
} // namespace entt
#endif

View File

@ -16,35 +16,47 @@ namespace entt {
namespace internal {
// waiting for C++20 (and std::popcount)
template<typename Type>
static constexpr int popcount(Type value) noexcept {
return value ? (int(value & 1) + popcount(value >> 1)) : 0;
}
template<typename, typename = void>
struct entt_traits;
template<typename Type>
struct entt_traits<Type, std::enable_if_t<std::is_enum_v<Type>>>
: entt_traits<std::underlying_type_t<Type>> {};
: entt_traits<std::underlying_type_t<Type>> {
using value_type = Type;
};
template<typename Type>
struct entt_traits<Type, std::enable_if_t<std::is_class_v<Type>>>
: entt_traits<typename Type::entity_type> {};
: entt_traits<typename Type::entity_type> {
using value_type = Type;
};
template<>
struct entt_traits<std::uint32_t> {
using value_type = std::uint32_t;
using entity_type = std::uint32_t;
using version_type = std::uint16_t;
static constexpr entity_type entity_mask = 0xFFFFF;
static constexpr entity_type version_mask = 0xFFF;
static constexpr std::size_t entity_shift = 20u;
};
template<>
struct entt_traits<std::uint64_t> {
using value_type = std::uint64_t;
using entity_type = std::uint64_t;
using version_type = std::uint32_t;
static constexpr entity_type entity_mask = 0xFFFFFFFF;
static constexpr entity_type version_mask = 0xFFFFFFFF;
static constexpr std::size_t entity_shift = 32u;
};
} // namespace internal
@ -55,24 +67,28 @@ struct entt_traits<std::uint64_t> {
*/
/**
* @brief Entity traits.
* @tparam Type Type of identifier.
* @brief Common basic entity traits implementation.
* @tparam Traits Actual entity traits to use.
*/
template<typename Type>
class entt_traits: internal::entt_traits<Type> {
using base_type = internal::entt_traits<Type>;
template<typename Traits>
class basic_entt_traits {
static constexpr auto length = internal::popcount(Traits::entity_mask);
static_assert(Traits::entity_mask && ((typename Traits::entity_type{1} << length) == (Traits::entity_mask + 1)), "Invalid entity mask");
static_assert((typename Traits::entity_type{1} << internal::popcount(Traits::version_mask)) == (Traits::version_mask + 1), "Invalid version mask");
public:
/*! @brief Value type. */
using value_type = Type;
using value_type = typename Traits::value_type;
/*! @brief Underlying entity type. */
using entity_type = typename base_type::entity_type;
using entity_type = typename Traits::entity_type;
/*! @brief Underlying version type. */
using version_type = typename base_type::version_type;
/*! @brief Reserved identifier. */
static constexpr entity_type reserved = base_type::entity_mask | (base_type::version_mask << base_type::entity_shift);
/*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */
static constexpr auto page_size = ENTT_SPARSE_PAGE;
using version_type = typename Traits::version_type;
/*! @brief Entity mask size. */
static constexpr entity_type entity_mask = Traits::entity_mask;
/*! @brief Version mask size */
static constexpr entity_type version_mask = Traits::version_mask;
/**
* @brief Converts an entity to its underlying type.
@ -89,7 +105,7 @@ public:
* @return The integral representation of the entity part.
*/
[[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept {
return (to_integral(value) & base_type::entity_mask);
return (to_integral(value) & entity_mask);
}
/**
@ -98,7 +114,17 @@ public:
* @return The integral representation of the version part.
*/
[[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept {
return (to_integral(value) >> base_type::entity_shift);
return static_cast<version_type>(to_integral(value) >> length);
}
/**
* @brief Returns the successor of a given identifier.
* @param value The identifier of which to return the successor.
* @return The successor of the given identifier.
*/
[[nodiscard]] static constexpr value_type next(const value_type value) noexcept {
const auto vers = to_version(value) + 1;
return construct(to_entity(value), static_cast<version_type>(vers + (vers == version_mask)));
}
/**
@ -112,7 +138,7 @@ public:
* @return A properly constructed identifier.
*/
[[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept {
return value_type{(entity & base_type::entity_mask) | (static_cast<entity_type>(version) << base_type::entity_shift)};
return value_type{(entity & entity_mask) | (static_cast<entity_type>(version) << length)};
}
/**
@ -126,11 +152,23 @@ public:
* @return A properly constructed identifier.
*/
[[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept {
constexpr auto mask = (base_type::version_mask << base_type::entity_shift);
return value_type{(lhs & base_type::entity_mask) | (rhs & mask)};
constexpr auto mask = (version_mask << length);
return value_type{(lhs & entity_mask) | (rhs & mask)};
}
};
/**
* @brief Entity traits.
* @tparam Type Type of identifier.
*/
template<typename Type>
struct entt_traits: basic_entt_traits<internal::entt_traits<Type>> {
/*! @brief Base type. */
using base_type = basic_entt_traits<internal::entt_traits<Type>>;
/*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */
static constexpr std::size_t page_size = ENTT_SPARSE_PAGE;
};
/**
* @copydoc entt_traits<Entity>::to_integral
* @tparam Entity The value type.
@ -167,8 +205,9 @@ struct null_t {
*/
template<typename Entity>
[[nodiscard]] constexpr operator Entity() const noexcept {
using entity_traits = entt_traits<Entity>;
return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
using traits_type = entt_traits<Entity>;
constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
return value;
}
/**
@ -197,8 +236,8 @@ struct null_t {
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
using entity_traits = entt_traits<Entity>;
return entity_traits::to_entity(entity) == entity_traits::to_entity(*this);
using traits_type = entt_traits<Entity>;
return traits_type::to_entity(entity) == traits_type::to_entity(*this);
}
/**
@ -246,8 +285,9 @@ struct tombstone_t {
*/
template<typename Entity>
[[nodiscard]] constexpr operator Entity() const noexcept {
using entity_traits = entt_traits<Entity>;
return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
using traits_type = entt_traits<Entity>;
constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
return value;
}
/**
@ -276,8 +316,8 @@ struct tombstone_t {
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
using entity_traits = entt_traits<Entity>;
return entity_traits::to_version(entity) == entity_traits::to_version(*this);
using traits_type = entt_traits<Entity>;
return traits_type::to_version(entity) == traits_type::to_version(*this);
}
/**

View File

@ -1,6 +1,7 @@
#ifndef ENTT_ENTITY_FWD_HPP
#define ENTT_ENTITY_FWD_HPP
#include <cstdint>
#include <memory>
#include <type_traits>
#include "../core/fwd.hpp"
@ -11,6 +12,14 @@ namespace entt {
/*! @brief Default entity identifier. */
enum class entity : id_type {};
/*! @brief Storage deletion policy. */
enum class deletion_policy : std::uint8_t {
/*! @brief Swap-and-pop deletion policy. */
swap_and_pop = 0u,
/*! @brief In-place deletion policy. */
in_place = 1u
};
template<typename Entity = entity, typename = std::allocator<Entity>>
class basic_sparse_set;
@ -18,18 +27,18 @@ template<typename Type, typename = entity, typename = std::allocator<Type>, type
class basic_storage;
template<typename Type>
class sigh_storage_mixin;
class sigh_mixin;
/**
* @brief Provides a common way to define storage types.
* @tparam Type Storage value type.
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<Type>, typename = void>
struct storage_type {
/*! @brief Type-to-storage conversion result. */
using type = sigh_storage_mixin<basic_storage<Type, Entity, Allocator>>;
using type = sigh_mixin<basic_storage<Type, Entity, Allocator>>;
};
/**
@ -42,7 +51,7 @@ using storage_type_t = typename storage_type<Args...>::type;
/**
* Type-to-storage conversion utility that preserves constness.
* @tparam Type Storage value type, eventually const.
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<std::remove_const_t<Type>>>
@ -70,7 +79,7 @@ class basic_runtime_view;
template<typename, typename, typename>
class basic_group;
template<typename>
template<typename, typename Mask = std::uint32_t, typename = std::allocator<Mask>>
class basic_observer;
template<typename>
@ -93,7 +102,10 @@ class basic_continuous_loader;
* @tparam Type List of types.
*/
template<typename... Type>
using exclude_t = type_list<Type...>;
struct exclude_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr exclude_t() {}
};
/**
* @brief Variable template for exclusion lists.
@ -107,7 +119,10 @@ inline constexpr exclude_t<Type...> exclude{};
* @tparam Type List of types.
*/
template<typename... Type>
using get_t = type_list<Type...>;
struct get_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr get_t() {}
};
/**
* @brief Variable template for lists of observed components.
@ -121,7 +136,10 @@ inline constexpr get_t<Type...> get{};
* @tparam Type List of types.
*/
template<typename... Type>
using owned_t = type_list<Type...>;
struct owned_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr owned_t() {}
};
/**
* @brief Variable template for lists of owned components.
@ -130,6 +148,39 @@ using owned_t = type_list<Type...>;
template<typename... Type>
inline constexpr owned_t<Type...> owned{};
/**
* @brief Applies a given _function_ to a get list and generate a new list.
* @tparam Type Types provided by the get list.
* @tparam Op Unary operation as template class with a type member named `type`.
*/
template<typename... Type, template<typename...> class Op>
struct type_list_transform<get_t<Type...>, Op> {
/*! @brief Resulting get list after applying the transform function. */
using type = get_t<typename Op<Type>::type...>;
};
/**
* @brief Applies a given _function_ to an exclude list and generate a new list.
* @tparam Type Types provided by the exclude list.
* @tparam Op Unary operation as template class with a type member named `type`.
*/
template<typename... Type, template<typename...> class Op>
struct type_list_transform<exclude_t<Type...>, Op> {
/*! @brief Resulting exclude list after applying the transform function. */
using type = exclude_t<typename Op<Type>::type...>;
};
/**
* @brief Applies a given _function_ to an owned list and generate a new list.
* @tparam Type Types provided by the owned list.
* @tparam Op Unary operation as template class with a type member named `type`.
*/
template<typename... Type, template<typename...> class Op>
struct type_list_transform<owned_t<Type...>, Op> {
/*! @brief Resulting owned list after applying the transform function. */
using type = owned_t<typename Op<Type>::type...>;
};
/*! @brief Alias declaration for the most common use case. */
using sparse_set = basic_sparse_set<>;

View File

@ -5,9 +5,10 @@
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/fwd.hpp"
#include "../core/iterator.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "component.hpp"
#include "entity.hpp"
#include "fwd.hpp"
#include "sparse_set.hpp"
@ -29,7 +30,7 @@ template<typename It, typename... Owned, typename... Get>
class extended_group_iterator<It, owned_t<Owned...>, get_t<Get...>> {
template<typename Type>
auto index_to_element([[maybe_unused]] Type &cpool) const {
if constexpr(ignore_as_empty_v<typename Type::value_type>) {
if constexpr(Type::traits_type::page_size == 0u) {
return std::make_tuple();
} else {
return std::forward_as_tuple(cpool.rbegin()[it.index()]);
@ -37,6 +38,7 @@ class extended_group_iterator<It, owned_t<Owned...>, get_t<Get...>> {
}
public:
using iterator_type = It;
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::declval<Owned>().get_as_tuple({})..., std::declval<Get>().get_as_tuple({})...));
using pointer = input_iterator_pointer<value_type>;
@ -68,6 +70,10 @@ public:
return operator*();
}
[[nodiscard]] constexpr iterator_type base() const noexcept {
return it;
}
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator==(const extended_group_iterator<Lhs...> &, const extended_group_iterator<Rhs...> &) noexcept;
@ -86,6 +92,164 @@ template<typename... Lhs, typename... Rhs>
return !(lhs == rhs);
}
struct group_descriptor {
using size_type = std::size_t;
virtual ~group_descriptor() = default;
virtual size_type owned(const id_type *, const size_type) const noexcept {
return 0u;
}
};
template<typename, typename, typename>
class group_handler;
template<typename... Owned, typename... Get, typename... Exclude>
class group_handler<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> final: public group_descriptor {
// nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here
static_assert(!std::disjunction_v<std::bool_constant<Owned::traits_type::in_place_delete>...>, "Groups do not support in-place delete");
static_assert(!std::disjunction_v<std::is_const<Owned>..., std::is_const<Get>..., std::is_const<Exclude>...>, "Const storage type not allowed");
using base_type = std::common_type_t<typename Owned::base_type..., typename Get::base_type..., typename Exclude::base_type...>;
using entity_type = typename base_type::entity_type;
void swap_elements(const std::size_t pos, const entity_type entt) {
std::apply([pos, entt](auto *...cpool) { (cpool->swap_elements(cpool->data()[pos], entt), ...); }, pools);
}
void push_on_construct(const entity_type entt) {
if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools)
&& std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
swap_elements(len++, entt);
}
}
void push_on_destroy(const entity_type entt) {
if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools)
&& std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) {
swap_elements(len++, entt);
}
}
void remove_if(const entity_type entt) {
if(std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < len)) {
swap_elements(--len, entt);
}
}
public:
using size_type = typename base_type::size_type;
group_handler(Owned &...opool, Get &...gpool, Exclude &...epool)
: pools{&opool..., &gpool...},
filter{&epool...},
len{} {
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools);
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter);
// we cannot iterate backwards because we want to leave behind valid entities in case of owned types
for(auto *first = std::get<0>(pools)->data(), *last = first + std::get<0>(pools)->size(); first != last; ++first) {
push_on_construct(*first);
}
}
size_type owned(const id_type *elem, const size_type length) const noexcept final {
size_type cnt = 0u;
for(auto pos = 0u; pos < length; ++pos) {
cnt += ((elem[pos] == entt::type_hash<typename Owned::value_type>::value()) || ...);
}
return cnt;
}
[[nodiscard]] size_type length() const noexcept {
return len;
}
template<typename Type>
Type pools_as() const noexcept {
return pools;
}
template<typename Type>
Type filter_as() const noexcept {
return filter;
}
private:
std::tuple<Owned *..., Get *...> pools;
std::tuple<Exclude *...> filter;
std::size_t len;
};
template<typename... Get, typename... Exclude>
class group_handler<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> final: public group_descriptor {
// nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here
static_assert(!std::disjunction_v<std::is_const<Get>..., std::is_const<Exclude>...>, "Const storage type not allowed");
using base_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>;
using entity_type = typename base_type::entity_type;
void push_on_construct(const entity_type entt) {
if(!elem.contains(entt)
&& std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools)
&& std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
elem.push(entt);
}
}
void push_on_destroy(const entity_type entt) {
if(!elem.contains(entt)
&& std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools)
&& std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) {
elem.push(entt);
}
}
void remove_if(const entity_type entt) {
elem.remove(entt);
}
public:
using common_type = base_type;
template<typename Alloc>
group_handler(const Alloc &alloc, Get &...gpool, Exclude &...epool)
: pools{&gpool...},
filter{&epool...},
elem{alloc} {
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools);
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter);
for(const auto entity: static_cast<base_type &>(*std::get<0>(pools))) {
push_on_construct(entity);
}
}
common_type &handle() noexcept {
return elem;
}
const common_type &handle() const noexcept {
return elem;
}
template<typename Type>
Type pools_as() const noexcept {
return pools;
}
template<typename Type>
Type filter_as() const noexcept {
return filter;
}
private:
std::tuple<Get *...> pools;
std::tuple<Exclude *...> filter;
base_type elem;
};
} // namespace internal
/**
@ -119,18 +283,28 @@ class basic_group;
* * The entity currently pointed is destroyed.
*
* In all other cases, modifying the pools iterated by the group in any way
* invalidates all the iterators and using them results in undefined behavior.
* invalidates all the iterators.
*
* @tparam Get Types of storage _observed_ by the group.
* @tparam Exclude Types of storage used to filter the group.
*/
template<typename... Get, typename... Exclude>
class basic_group<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> {
using underlying_type = std::common_type_t<typename Get::entity_type..., typename Exclude::entity_type...>;
using basic_common_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>;
using base_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>;
using underlying_type = typename base_type::entity_type;
template<typename Type>
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::value_type...>>;
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::value_type..., typename Exclude::value_type...>>;
auto pools() const noexcept {
using return_type = std::tuple<Get *...>;
return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
}
auto filter() const noexcept {
using return_type = std::tuple<Exclude *...>;
return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
}
public:
/*! @brief Underlying entity identifier. */
@ -138,53 +312,59 @@ public:
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Common type among all storage types. */
using base_type = basic_common_type;
using common_type = base_type;
/*! @brief Random access iterator type. */
using iterator = typename base_type::iterator;
using iterator = typename common_type::iterator;
/*! @brief Reversed iterator type. */
using reverse_iterator = typename base_type::reverse_iterator;
using reverse_iterator = typename common_type::reverse_iterator;
/*! @brief Iterable group type. */
using iterable = iterable_adaptor<internal::extended_group_iterator<iterator, owned_t<>, get_t<Get...>>>;
/*! @brief Group handler type. */
using handler = internal::group_handler<owned_t<>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>;
/*! @brief Default constructor to use to create empty, invalid groups. */
basic_group() noexcept
: handler{} {}
: descriptor{} {}
/**
* @brief Constructs a group from a set of storage classes.
* @param ref The actual entities to iterate.
* @param gpool Storage types to iterate _observed_ by the group.
* @param ref A reference to a group handler.
*/
basic_group(basic_common_type &ref, Get &...gpool) noexcept
: handler{&ref},
pools{&gpool...} {}
basic_group(handler &ref) noexcept
: descriptor{&ref} {}
/**
* @brief Returns a const reference to the underlying handler.
* @return A const reference to the underlying handler.
* @brief Returns the leading storage of a group.
* @return The leading storage of the group.
*/
[[nodiscard]] const base_type &handle() const noexcept {
return *handler;
[[nodiscard]] const common_type &handle() const noexcept {
return descriptor->handle();
}
/**
* @brief Returns the storage for a given component type.
* @brief Returns the storage for a given component type, if any.
* @tparam Type Type of component of which to return the storage.
* @return The storage for the given component type.
*/
template<typename Type>
[[nodiscard]] decltype(auto) storage() const noexcept {
[[nodiscard]] auto *storage() const noexcept {
return storage<index_of<Type>>();
}
/**
* @brief Returns the storage for a given index.
* @brief Returns the storage for a given index, if any.
* @tparam Index Index of the storage to return.
* @return The storage for the given index.
*/
template<std::size_t Index>
[[nodiscard]] decltype(auto) storage() const noexcept {
return *std::get<Index>(pools);
[[nodiscard]] auto *storage() const noexcept {
constexpr auto offset = sizeof...(Get);
if constexpr(Index < offset) {
return std::get<Index>(pools());
} else {
return std::get<Index - offset>(filter());
}
}
/**
@ -192,7 +372,7 @@ public:
* @return Number of entities that are part of the group.
*/
[[nodiscard]] size_type size() const noexcept {
return *this ? handler->size() : size_type{};
return *this ? handle().size() : size_type{};
}
/**
@ -201,13 +381,13 @@ public:
* @return Capacity of the group.
*/
[[nodiscard]] size_type capacity() const noexcept {
return *this ? handler->capacity() : size_type{};
return *this ? handle().capacity() : size_type{};
}
/*! @brief Requests the removal of unused capacity. */
void shrink_to_fit() {
if(*this) {
handler->shrink_to_fit();
descriptor->handle().shrink_to_fit();
}
}
@ -216,60 +396,48 @@ public:
* @return True if the group is empty, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
return !*this || handler->empty();
return !*this || handle().empty();
}
/**
* @brief Returns an iterator to the first entity of the group.
*
* The returned iterator points to the first entity of the group. If the
* group is empty, the returned iterator will be equal to `end()`.
* If the group is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first entity of the group.
*/
[[nodiscard]] iterator begin() const noexcept {
return *this ? handler->begin() : iterator{};
return *this ? handle().begin() : iterator{};
}
/**
* @brief Returns an iterator that is past the last entity of the group.
*
* The returned iterator points to the entity following the last entity of
* the group. Attempting to dereference the returned iterator results in
* undefined behavior.
*
* @return An iterator to the entity following the last entity of the
* group.
*/
[[nodiscard]] iterator end() const noexcept {
return *this ? handler->end() : iterator{};
return *this ? handle().end() : iterator{};
}
/**
* @brief Returns an iterator to the first entity of the reversed group.
*
* The returned iterator points to the first entity of the reversed group.
* If the group is empty, the returned iterator will be equal to `rend()`.
*
* @return An iterator to the first entity of the reversed group.
*/
[[nodiscard]] reverse_iterator rbegin() const noexcept {
return *this ? handler->rbegin() : reverse_iterator{};
return *this ? handle().rbegin() : reverse_iterator{};
}
/**
* @brief Returns an iterator that is past the last entity of the reversed
* group.
*
* The returned iterator points to the entity following the last entity of
* the reversed group. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @return An iterator to the entity following the last entity of the
* reversed group.
*/
[[nodiscard]] reverse_iterator rend() const noexcept {
return *this ? handler->rend() : reverse_iterator{};
return *this ? handle().rend() : reverse_iterator{};
}
/**
@ -299,8 +467,7 @@ public:
* iterator otherwise.
*/
[[nodiscard]] iterator find(const entity_type entt) const noexcept {
const auto it = *this ? handler->find(entt) : iterator{};
return it != end() && *it == entt ? it : end();
return *this ? handle().find(entt) : iterator{};
}
/**
@ -317,7 +484,7 @@ public:
* @return True if the group is properly initialized, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return handler != nullptr;
return descriptor != nullptr;
}
/**
@ -326,32 +493,47 @@ public:
* @return True if the group contains the given entity, false otherwise.
*/
[[nodiscard]] bool contains(const entity_type entt) const noexcept {
return *this && handler->contains(entt);
return *this && handle().contains(entt);
}
/**
* @brief Returns the components assigned to the given entity.
*
* Prefer this function instead of `registry::get` during iterations. It has
* far better performance than its counterpart.
*
* @warning
* Attempting to use an invalid component type results in a compilation
* error. Attempting to use an entity that doesn't belong to the group
* results in undefined behavior.
* Attempting to use an entity that doesn't belong to the group results in
* undefined behavior.
*
* @tparam Type Types of components to get.
* @tparam Type Type of the component to get.
* @tparam Other Other types of components to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
*/
template<typename... Type>
template<typename Type, typename... Other>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
if constexpr(sizeof...(Type) == 0) {
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools);
} else if constexpr(sizeof...(Type) == 1) {
return (std::get<index_of<Type>>(pools)->get(entt), ...);
return get<index_of<Type>, index_of<Other>...>(entt);
}
/**
* @brief Returns the components assigned to the given entity.
*
* @warning
* Attempting to use an entity that doesn't belong to the groups results in
* undefined behavior.
*
* @tparam Index Indexes of the components to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
*/
template<std::size_t... Index>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
const auto cpools = pools();
if constexpr(sizeof...(Index) == 0) {
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools);
} else if constexpr(sizeof...(Index) == 1) {
return (std::get<Index>(cpools)->get(entt), ...);
} else {
return std::tuple_cat(std::get<index_of<Type>>(pools)->get_as_tuple(entt)...);
return std::tuple_cat(std::get<Index>(cpools)->get_as_tuple(entt)...);
}
}
@ -402,16 +584,13 @@ public:
* @return An iterable object to use to _visit_ the group.
*/
[[nodiscard]] iterable each() const noexcept {
return iterable{{begin(), pools}, {end(), pools}};
const auto cpools = pools();
return iterable{{begin(), cpools}, {end(), cpools}};
}
/**
* @brief Sort a group according to the given comparison function.
*
* Sort the group so that iterating it with a couple of iterators returns
* entities and components in the expected order. See `begin` and `end` for
* more details.
*
* The comparison function object must return `true` if the first element
* is _less_ than the second one, `false` otherwise. The signature of the
* comparison function should be equivalent to one of the following:
@ -433,7 +612,8 @@ public:
* * An iterator past the last element of the range to sort.
* * A comparison function to use to compare the elements.
*
* @tparam Type Optional types of components to compare.
* @tparam Type Optional type of component to compare.
* @tparam Other Other optional types of components to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@ -441,52 +621,60 @@ public:
* @param algo A valid sort function object.
* @param args Arguments to forward to the sort function object, if any.
*/
template<typename... Type, typename Compare, typename Sort = std_sort, typename... Args>
template<typename Type, typename... Other, typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
sort<index_of<Type>, index_of<Other>...>(std::move(compare), std::move(algo), std::forward<Args>(args)...);
}
/**
* @brief Sort a group according to the given comparison function.
*
* @sa sort
*
* @tparam Index Optional indexes of components to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
* @param compare A valid comparison function object.
* @param algo A valid sort function object.
* @param args Arguments to forward to the sort function object, if any.
*/
template<std::size_t... Index, typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
if(*this) {
if constexpr(sizeof...(Type) == 0) {
if constexpr(sizeof...(Index) == 0) {
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
handler->sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
descriptor->handle().sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
} else {
auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) {
if constexpr(sizeof...(Type) == 1) {
return compare((std::get<index_of<Type>>(pools)->get(lhs), ...), (std::get<index_of<Type>>(pools)->get(rhs), ...));
auto comp = [&compare, cpools = pools()](const entity_type lhs, const entity_type rhs) {
if constexpr(sizeof...(Index) == 1) {
return compare((std::get<Index>(cpools)->get(lhs), ...), (std::get<Index>(cpools)->get(rhs), ...));
} else {
return compare(std::forward_as_tuple(std::get<index_of<Type>>(pools)->get(lhs)...), std::forward_as_tuple(std::get<index_of<Type>>(pools)->get(rhs)...));
return compare(std::forward_as_tuple(std::get<Index>(cpools)->get(lhs)...), std::forward_as_tuple(std::get<Index>(cpools)->get(rhs)...));
}
};
handler->sort(std::move(comp), std::move(algo), std::forward<Args>(args)...);
descriptor->handle().sort(std::move(comp), std::move(algo), std::forward<Args>(args)...);
}
}
}
/**
* @brief Sort the shared pool of entities according to the given component.
* @brief Sort the shared pool of entities according to a given storage.
*
* Non-owning groups of the same type share with the registry a pool of
* entities with its own order that doesn't depend on the order of any pool
* of components. Users can order the underlying data structure so that it
* respects the order of the pool of the given component.
*
* @note
* The shared pool of entities and thus its order is affected by the changes
* to each and every pool that it tracks. Therefore changes to those pools
* can quickly ruin the order imposed to the pool of entities shared between
* the non-owning groups.
* to each and every pool that it tracks.
*
* @tparam Type Type of component to use to impose the order.
* @param other The storage to use to impose the order.
*/
template<typename Type>
void sort() const {
void sort_as(const common_type &other) const {
if(*this) {
handler->respect(*std::get<index_of<Type>>(pools));
descriptor->handle().sort_as(other);
}
}
private:
base_type *const handler;
const std::tuple<Get *...> pools;
handler *descriptor;
};
/**
@ -514,7 +702,7 @@ private:
* * The entity currently pointed is destroyed.
*
* In all other cases, modifying the pools iterated by the group in any way
* invalidates all the iterators and using them results in undefined behavior.
* invalidates all the iterators.
*
* @tparam Owned Types of storage _owned_ by the group.
* @tparam Get Types of storage _observed_ by the group.
@ -522,11 +710,21 @@ private:
*/
template<typename... Owned, typename... Get, typename... Exclude>
class basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> {
using underlying_type = std::common_type_t<typename Owned::entity_type..., typename Get::entity_type..., typename Exclude::entity_type...>;
using basic_common_type = std::common_type_t<typename Owned::base_type..., typename Get::base_type..., typename Exclude::base_type...>;
using base_type = std::common_type_t<typename Owned::base_type..., typename Get::base_type..., typename Exclude::base_type...>;
using underlying_type = typename base_type::entity_type;
template<typename Type>
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Owned::value_type..., typename Get::value_type...>>;
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Owned::value_type..., typename Get::value_type..., typename Exclude::value_type...>>;
auto pools() const noexcept {
using return_type = std::tuple<Owned *..., Get *...>;
return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
}
auto filter() const noexcept {
using return_type = std::tuple<Exclude *...>;
return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
}
public:
/*! @brief Underlying entity identifier. */
@ -534,46 +732,59 @@ public:
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Common type among all storage types. */
using base_type = basic_common_type;
using common_type = base_type;
/*! @brief Random access iterator type. */
using iterator = typename base_type::iterator;
using iterator = typename common_type::iterator;
/*! @brief Reversed iterator type. */
using reverse_iterator = typename base_type::reverse_iterator;
using reverse_iterator = typename common_type::reverse_iterator;
/*! @brief Iterable group type. */
using iterable = iterable_adaptor<internal::extended_group_iterator<iterator, owned_t<Owned...>, get_t<Get...>>>;
/*! @brief Group handler type. */
using handler = internal::group_handler<owned_t<std::remove_const_t<Owned>...>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>;
/*! @brief Default constructor to use to create empty, invalid groups. */
basic_group() noexcept
: length{} {}
: descriptor{} {}
/**
* @brief Constructs a group from a set of storage classes.
* @param extent The actual number of entities to iterate.
* @param opool Storage types to iterate _owned_ by the group.
* @param gpool Storage types to iterate _observed_ by the group.
* @param ref A reference to a group handler.
*/
basic_group(const std::size_t &extent, Owned &...opool, Get &...gpool) noexcept
: pools{&opool..., &gpool...},
length{&extent} {}
basic_group(handler &ref) noexcept
: descriptor{&ref} {}
/**
* @brief Returns the storage for a given component type.
* @brief Returns the leading storage of a group.
* @return The leading storage of the group.
*/
[[nodiscard]] const common_type &handle() const noexcept {
return *storage<0>();
}
/**
* @brief Returns the storage for a given component type, if any.
* @tparam Type Type of component of which to return the storage.
* @return The storage for the given component type.
*/
template<typename Type>
[[nodiscard]] decltype(auto) storage() const noexcept {
[[nodiscard]] auto *storage() const noexcept {
return storage<index_of<Type>>();
}
/**
* @brief Returns the storage for a given index.
* @brief Returns the storage for a given index, if any.
* @tparam Index Index of the storage to return.
* @return The storage for the given index.
*/
template<std::size_t Index>
[[nodiscard]] decltype(auto) storage() const noexcept {
return *std::get<Index>(pools);
[[nodiscard]] auto *storage() const noexcept {
constexpr auto offset = sizeof...(Owned) + sizeof...(Get);
if constexpr(Index < offset) {
return std::get<Index>(pools());
} else {
return std::get<Index - offset>(filter());
}
}
/**
@ -581,7 +792,7 @@ public:
* @return Number of entities that that are part of the group.
*/
[[nodiscard]] size_type size() const noexcept {
return *this ? *length : size_type{};
return *this ? descriptor->length() : size_type{};
}
/**
@ -589,60 +800,48 @@ public:
* @return True if the group is empty, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
return !*this || !*length;
return !*this || !descriptor->length();
}
/**
* @brief Returns an iterator to the first entity of the group.
*
* The returned iterator points to the first entity of the group. If the
* group is empty, the returned iterator will be equal to `end()`.
* If the group is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first entity of the group.
*/
[[nodiscard]] iterator begin() const noexcept {
return *this ? (std::get<0>(pools)->base_type::end() - *length) : iterator{};
return *this ? (handle().end() - descriptor->length()) : iterator{};
}
/**
* @brief Returns an iterator that is past the last entity of the group.
*
* The returned iterator points to the entity following the last entity of
* the group. Attempting to dereference the returned iterator results in
* undefined behavior.
*
* @return An iterator to the entity following the last entity of the
* group.
*/
[[nodiscard]] iterator end() const noexcept {
return *this ? std::get<0>(pools)->base_type::end() : iterator{};
return *this ? handle().end() : iterator{};
}
/**
* @brief Returns an iterator to the first entity of the reversed group.
*
* The returned iterator points to the first entity of the reversed group.
* If the group is empty, the returned iterator will be equal to `rend()`.
*
* @return An iterator to the first entity of the reversed group.
*/
[[nodiscard]] reverse_iterator rbegin() const noexcept {
return *this ? std::get<0>(pools)->base_type::rbegin() : reverse_iterator{};
return *this ? handle().rbegin() : reverse_iterator{};
}
/**
* @brief Returns an iterator that is past the last entity of the reversed
* group.
*
* The returned iterator points to the entity following the last entity of
* the reversed group. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @return An iterator to the entity following the last entity of the
* reversed group.
*/
[[nodiscard]] reverse_iterator rend() const noexcept {
return *this ? (std::get<0>(pools)->base_type::rbegin() + *length) : reverse_iterator{};
return *this ? (handle().rbegin() + descriptor->length()) : reverse_iterator{};
}
/**
@ -672,8 +871,8 @@ public:
* iterator otherwise.
*/
[[nodiscard]] iterator find(const entity_type entt) const noexcept {
const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{};
return it != end() && it >= begin() && *it == entt ? it : end();
const auto it = *this ? handle().find(entt) : iterator{};
return it >= begin() ? it : iterator{};
}
/**
@ -690,7 +889,7 @@ public:
* @return True if the group is properly initialized, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return length != nullptr;
return descriptor != nullptr;
}
/**
@ -699,32 +898,47 @@ public:
* @return True if the group contains the given entity, false otherwise.
*/
[[nodiscard]] bool contains(const entity_type entt) const noexcept {
return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length));
return *this && handle().contains(entt) && (handle().index(entt) < (descriptor->length()));
}
/**
* @brief Returns the components assigned to the given entity.
*
* Prefer this function instead of `registry::get` during iterations. It has
* far better performance than its counterpart.
*
* @warning
* Attempting to use an invalid component type results in a compilation
* error. Attempting to use an entity that doesn't belong to the group
* results in undefined behavior.
* Attempting to use an entity that doesn't belong to the group results in
* undefined behavior.
*
* @tparam Type Types of components to get.
* @tparam Type Type of the component to get.
* @tparam Other Other types of components to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
*/
template<typename... Type>
template<typename Type, typename... Other>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
if constexpr(sizeof...(Type) == 0) {
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools);
} else if constexpr(sizeof...(Type) == 1) {
return (std::get<index_of<Type>>(pools)->get(entt), ...);
return get<index_of<Type>, index_of<Other>...>(entt);
}
/**
* @brief Returns the components assigned to the given entity.
*
* @warning
* Attempting to use an entity that doesn't belong to the groups results in
* undefined behavior.
*
* @tparam Index Indexes of the components to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
*/
template<std::size_t... Index>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
const auto cpools = pools();
if constexpr(sizeof...(Index) == 0) {
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools);
} else if constexpr(sizeof...(Index) == 1) {
return (std::get<Index>(cpools)->get(entt), ...);
} else {
return std::tuple_cat(std::get<index_of<Type>>(pools)->get_as_tuple(entt)...);
return std::tuple_cat(std::get<Index>(cpools)->get_as_tuple(entt)...);
}
}
@ -775,16 +989,13 @@ public:
* @return An iterable object to use to _visit_ the group.
*/
[[nodiscard]] iterable each() const noexcept {
return {{begin(), pools}, {end(), pools}};
const auto cpools = pools();
return {{begin(), cpools}, {end(), cpools}};
}
/**
* @brief Sort a group according to the given comparison function.
*
* Sort the group so that iterating it with a couple of iterators returns
* entities and components in the expected order. See `begin` and `end` for
* more details.
*
* The comparison function object must return `true` if the first element
* is _less_ than the second one, `false` otherwise. The signature of the
* comparison function should be equivalent to one of the following:
@ -807,7 +1018,8 @@ public:
* * An iterator past the last element of the range to sort.
* * A comparison function to use to compare the elements.
*
* @tparam Type Optional types of components to compare.
* @tparam Type Optional type of component to compare.
* @tparam Other Other optional types of components to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@ -815,36 +1027,56 @@ public:
* @param algo A valid sort function object.
* @param args Arguments to forward to the sort function object, if any.
*/
template<typename... Type, typename Compare, typename Sort = std_sort, typename... Args>
template<typename Type, typename... Other, typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const {
if constexpr(sizeof...(Type) == 0) {
sort<index_of<Type>, index_of<Other>...>(std::move(compare), std::move(algo), std::forward<Args>(args)...);
}
/**
* @brief Sort a group according to the given comparison function.
*
* @sa sort
*
* @tparam Index Optional indexes of components to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
* @param compare A valid comparison function object.
* @param algo A valid sort function object.
* @param args Arguments to forward to the sort function object, if any.
*/
template<std::size_t... Index, typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const {
const auto cpools = pools();
if constexpr(sizeof...(Index) == 0) {
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
std::get<0>(pools)->sort_n(*length, std::move(compare), std::move(algo), std::forward<Args>(args)...);
storage<0>()->sort_n(descriptor->length(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
} else {
auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) {
if constexpr(sizeof...(Type) == 1) {
return compare((std::get<index_of<Type>>(pools)->get(lhs), ...), (std::get<index_of<Type>>(pools)->get(rhs), ...));
auto comp = [&compare, &cpools](const entity_type lhs, const entity_type rhs) {
if constexpr(sizeof...(Index) == 1) {
return compare((std::get<Index>(cpools)->get(lhs), ...), (std::get<Index>(cpools)->get(rhs), ...));
} else {
return compare(std::forward_as_tuple(std::get<index_of<Type>>(pools)->get(lhs)...), std::forward_as_tuple(std::get<index_of<Type>>(pools)->get(rhs)...));
return compare(std::forward_as_tuple(std::get<Index>(cpools)->get(lhs)...), std::forward_as_tuple(std::get<Index>(cpools)->get(rhs)...));
}
};
std::get<0>(pools)->sort_n(*length, std::move(comp), std::move(algo), std::forward<Args>(args)...);
storage<0>()->sort_n(descriptor->length(), std::move(comp), std::move(algo), std::forward<Args>(args)...);
}
std::apply([this](auto *head, auto *...other) {
for(auto next = *length; next; --next) {
auto cb = [this](auto *head, auto *...other) {
for(auto next = descriptor->length(); next; --next) {
const auto pos = next - 1;
[[maybe_unused]] const auto entt = head->data()[pos];
(other->swap_elements(other->data()[pos], entt), ...);
}
},
pools);
};
std::apply(cb, cpools);
}
private:
const std::tuple<Owned *..., Get *...> pools;
const size_type *const length;
handler *descriptor;
};
} // namespace entt

View File

@ -43,7 +43,9 @@ public:
: entt{value},
it{from},
last{to} {
while(it != last && !it->second.contains(entt)) { ++it; }
while(it != last && !it->second.contains(entt)) {
++it;
}
}
constexpr handle_storage_iterator &operator++() noexcept {
@ -141,7 +143,7 @@ struct basic_handle {
/**
* @brief Constructs a const handle from a non-const one.
* @tparam Other A valid entity type (see entt_traits for more details).
* @tparam Other A valid entity type.
* @tparam Args Scope of the handle to construct.
* @return A const handle referring to the same registry and the same
* entity.
@ -196,7 +198,7 @@ struct basic_handle {
/*! @brief Destroys the entity associated with a handle. */
void destroy() {
reg->destroy(entt);
reg->destroy(std::exchange(entt, null));
}
/**
@ -204,7 +206,7 @@ struct basic_handle {
* @param version A desired version upon destruction.
*/
void destroy(const version_type version) {
reg->destroy(entt, version);
reg->destroy(std::exchange(entt, null), version);
}
/**

View File

@ -3,10 +3,10 @@
#include <memory>
#include <type_traits>
#include <utility>
#include "../core/fwd.hpp"
#include "../core/type_traits.hpp"
#include "../signal/delegate.hpp"
#include "component.hpp"
#include "fwd.hpp"
#include "group.hpp"
#include "view.hpp"
@ -28,7 +28,7 @@ public:
/*! @brief Type of registry to convert. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = std::remove_const_t<typename registry_type::entity_type>;
using entity_type = typename registry_type::entity_type;
/**
* @brief Constructs a converter for a given registry.
@ -71,7 +71,7 @@ public:
/*! @brief Type of registry to convert. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = std::remove_const_t<typename registry_type::entity_type>;
using entity_type = typename registry_type::entity_type;
/**
* @brief Constructs a converter for a given registry.
@ -126,19 +126,133 @@ void invoke(Registry &reg, const typename Registry::entity_type entt) {
*/
template<typename Registry, typename Component>
typename Registry::entity_type to_entity(const Registry &reg, const Component &instance) {
const auto &storage = reg.template storage<Component>();
const typename Registry::base_type &base = storage;
const auto *addr = std::addressof(instance);
if(const auto *storage = reg.template storage<Component>(); storage) {
constexpr auto page_size = std::remove_const_t<std::remove_pointer_t<decltype(storage)>>::traits_type::page_size;
const typename Registry::common_type &base = *storage;
const auto *addr = std::addressof(instance);
for(auto it = base.rbegin(), last = base.rend(); it < last; it += component_traits<Component>::page_size) {
if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(component_traits<Component>::page_size)) {
return *(it + dist);
for(auto it = base.rbegin(), last = base.rend(); it < last; it += page_size) {
if(const auto dist = (addr - std::addressof(storage->get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(page_size)) {
return *(it + dist);
}
}
}
return null;
}
/*! @brief Primary template isn't defined on purpose. */
template<typename...>
struct sigh_helper;
/**
* @brief Signal connection helper for registries.
* @tparam Registry Basic registry type.
*/
template<typename Registry>
struct sigh_helper<Registry> {
/*! @brief Registry type. */
using registry_type = Registry;
/**
* @brief Constructs a helper for a given registry.
* @param ref A valid reference to a registry.
*/
sigh_helper(registry_type &ref)
: bucket{&ref} {}
/**
* @brief Binds a properly initialized helper to a given signal type.
* @tparam Type Type of signal to bind the helper to.
* @param id Optional name for the underlying storage to use.
* @return A helper for a given registry and signal type.
*/
template<typename Type>
auto with(const id_type id = type_hash<Type>::value()) noexcept {
return sigh_helper<registry_type, Type>{*bucket, id};
}
/**
* @brief Returns a reference to the underlying registry.
* @return A reference to the underlying registry.
*/
[[nodiscard]] registry_type &registry() noexcept {
return *bucket;
}
private:
registry_type *bucket;
};
/**
* @brief Signal connection helper for registries.
* @tparam Registry Basic registry type.
* @tparam Type Type of signal to connect listeners to.
*/
template<typename Registry, typename Type>
struct sigh_helper<Registry, Type> final: sigh_helper<Registry> {
/*! @brief Registry type. */
using registry_type = Registry;
/**
* @brief Constructs a helper for a given registry.
* @param ref A valid reference to a registry.
* @param id Optional name for the underlying storage to use.
*/
sigh_helper(registry_type &ref, const id_type id = type_hash<Type>::value())
: sigh_helper<Registry>{ref},
name{id} {}
/**
* @brief Forwards the call to `on_construct` on the underlying storage.
* @tparam Candidate Function or member to connect.
* @tparam Args Type of class or type of payload, if any.
* @param args A valid object that fits the purpose, if any.
* @return This helper.
*/
template<auto Candidate, typename... Args>
auto on_construct(Args &&...args) {
this->registry().template on_construct<Type>(name).template connect<Candidate>(std::forward<Args>(args)...);
return *this;
}
/**
* @brief Forwards the call to `on_update` on the underlying storage.
* @tparam Candidate Function or member to connect.
* @tparam Args Type of class or type of payload, if any.
* @param args A valid object that fits the purpose, if any.
* @return This helper.
*/
template<auto Candidate, typename... Args>
auto on_update(Args &&...args) {
this->registry().template on_update<Type>(name).template connect<Candidate>(std::forward<Args>(args)...);
return *this;
}
/**
* @brief Forwards the call to `on_destroy` on the underlying storage.
* @tparam Candidate Function or member to connect.
* @tparam Args Type of class or type of payload, if any.
* @param args A valid object that fits the purpose, if any.
* @return This helper.
*/
template<auto Candidate, typename... Args>
auto on_destroy(Args &&...args) {
this->registry().template on_destroy<Type>(name).template connect<Candidate>(std::forward<Args>(args)...);
return *this;
}
private:
id_type name;
};
/**
* @brief Deduction guide.
* @tparam Registry Basic registry type.
*/
template<typename Registry>
sigh_helper(Registry &) -> sigh_helper<Registry>;
} // namespace entt
#endif

View File

@ -0,0 +1,293 @@
#ifndef ENTT_ENTITY_MIXIN_HPP
#define ENTT_ENTITY_MIXIN_HPP
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/any.hpp"
#include "../signal/sigh.hpp"
#include "entity.hpp"
#include "fwd.hpp"
namespace entt {
/**
* @brief Mixin type used to add signal support to storage types.
*
* The function type of a listener is equivalent to:
*
* @code{.cpp}
* void(basic_registry<entity_type> &, entity_type);
* @endcode
*
* This applies to all signals made available.
*
* @tparam Type The type of the underlying storage.
*/
template<typename Type>
class sigh_mixin final: public Type {
using underlying_type = Type;
using basic_registry_type = basic_registry<typename underlying_type::entity_type, typename underlying_type::base_type::allocator_type>;
using sigh_type = sigh<void(basic_registry_type &, const typename underlying_type::entity_type), typename underlying_type::allocator_type>;
using underlying_iterator = typename underlying_type::base_type::basic_iterator;
basic_registry_type &owner_or_assert() const noexcept {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
return *owner;
}
void pop(underlying_iterator first, underlying_iterator last) final {
if(auto &reg = owner_or_assert(); destruction.empty()) {
underlying_type::pop(first, last);
} else {
for(; first != last; ++first) {
const auto entt = *first;
destruction.publish(reg, entt);
const auto it = underlying_type::find(entt);
underlying_type::pop(it, it + 1u);
}
}
}
void pop_all() final {
if(auto &reg = owner_or_assert(); !destruction.empty()) {
for(auto pos = underlying_type::each().begin().base().index(); !(pos < 0); --pos) {
if constexpr(underlying_type::traits_type::in_place_delete) {
if(const auto entt = underlying_type::operator[](static_cast<typename underlying_type::size_type>(pos)); entt != tombstone) {
destruction.publish(reg, entt);
}
} else {
destruction.publish(reg, underlying_type::operator[](static_cast<typename underlying_type::size_type>(pos)));
}
}
}
underlying_type::pop_all();
}
underlying_iterator try_emplace(const typename underlying_type::entity_type entt, const bool force_back, const void *value) final {
const auto it = underlying_type::try_emplace(entt, force_back, value);
if(auto &reg = owner_or_assert(); it != underlying_type::base_type::end()) {
construction.publish(reg, *it);
}
return it;
}
public:
/*! @brief Allocator type. */
using allocator_type = typename underlying_type::allocator_type;
/*! @brief Underlying entity identifier. */
using entity_type = typename underlying_type::entity_type;
/*! @brief Expected registry type. */
using registry_type = basic_registry_type;
/*! @brief Default constructor. */
sigh_mixin()
: sigh_mixin{allocator_type{}} {}
/**
* @brief Constructs an empty storage with a given allocator.
* @param allocator The allocator to use.
*/
explicit sigh_mixin(const allocator_type &allocator)
: underlying_type{allocator},
owner{},
construction{allocator},
destruction{allocator},
update{allocator} {}
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
sigh_mixin(sigh_mixin &&other) noexcept
: underlying_type{std::move(other)},
owner{other.owner},
construction{std::move(other.construction)},
destruction{std::move(other.destruction)},
update{std::move(other.update)} {}
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
sigh_mixin(sigh_mixin &&other, const allocator_type &allocator) noexcept
: underlying_type{std::move(other), allocator},
owner{other.owner},
construction{std::move(other.construction), allocator},
destruction{std::move(other.destruction), allocator},
update{std::move(other.update), allocator} {}
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This storage.
*/
sigh_mixin &operator=(sigh_mixin &&other) noexcept {
underlying_type::operator=(std::move(other));
owner = other.owner;
construction = std::move(other.construction);
destruction = std::move(other.destruction);
update = std::move(other.update);
return *this;
}
/**
* @brief Exchanges the contents with those of a given storage.
* @param other Storage to exchange the content with.
*/
void swap(sigh_mixin &other) {
using std::swap;
underlying_type::swap(other);
swap(owner, other.owner);
swap(construction, other.construction);
swap(destruction, other.destruction);
swap(update, other.update);
}
/**
* @brief Returns a sink object.
*
* The sink returned by this function can be used to receive notifications
* whenever a new instance is created and assigned to an entity.<br/>
* Listeners are invoked after the object has been assigned to the entity.
*
* @sa sink
*
* @return A temporary sink object.
*/
[[nodiscard]] auto on_construct() noexcept {
return sink{construction};
}
/**
* @brief Returns a sink object.
*
* The sink returned by this function can be used to receive notifications
* whenever an instance is explicitly updated.<br/>
* Listeners are invoked after the object has been updated.
*
* @sa sink
*
* @return A temporary sink object.
*/
[[nodiscard]] auto on_update() noexcept {
return sink{update};
}
/**
* @brief Returns a sink object.
*
* The sink returned by this function can be used to receive notifications
* whenever an instance is removed from an entity and thus destroyed.<br/>
* Listeners are invoked before the object has been removed from the entity.
*
* @sa sink
*
* @return A temporary sink object.
*/
[[nodiscard]] auto on_destroy() noexcept {
return sink{destruction};
}
/**
* @brief Emplace elements into a storage.
*
* The behavior of this operation depends on the underlying storage type
* (for example, components vs entities).<br/>
* Refer to the specific documentation for more details.
*
* @return A return value as returned by the underlying storage.
*/
auto emplace() {
const auto entt = underlying_type::emplace();
construction.publish(owner_or_assert(), entt);
return entt;
}
/**
* @brief Emplace elements into a storage.
*
* The behavior of this operation depends on the underlying storage type
* (for example, components vs entities).<br/>
* Refer to the specific documentation for more details.
*
* @tparam Args Types of arguments to forward to the underlying storage.
* @param hint A valid identifier.
* @param args Parameters to forward to the underlying storage.
* @return A return value as returned by the underlying storage.
*/
template<typename... Args>
decltype(auto) emplace(const entity_type hint, Args &&...args) {
if constexpr(std::is_same_v<typename underlying_type::value_type, typename underlying_type::entity_type>) {
const auto entt = underlying_type::emplace(hint, std::forward<Args>(args)...);
construction.publish(owner_or_assert(), entt);
return entt;
} else {
underlying_type::emplace(hint, std::forward<Args>(args)...);
construction.publish(owner_or_assert(), hint);
return this->get(hint);
}
}
/**
* @brief Patches the given instance for an entity.
* @tparam Func Types of the function objects to invoke.
* @param entt A valid identifier.
* @param func Valid function objects.
* @return A reference to the patched instance.
*/
template<typename... Func>
decltype(auto) patch(const entity_type entt, Func &&...func) {
underlying_type::patch(entt, std::forward<Func>(func)...);
update.publish(owner_or_assert(), entt);
return this->get(entt);
}
/**
* @brief Emplace elements into a storage.
*
* The behavior of this operation depends on the underlying storage type
* (for example, components vs entities).<br/>
* Refer to the specific documentation for more details.
*
* @tparam It Iterator type (as required by the underlying storage type).
* @tparam Args Types of arguments to forward to the underlying storage.
* @param first An iterator to the first element of the range.
* @param last An iterator past the last element of the range.
* @param args Parameters to use to forward to the underlying storage.
*/
template<typename It, typename... Args>
void insert(It first, It last, Args &&...args) {
underlying_type::insert(first, last, std::forward<Args>(args)...);
if(auto &reg = owner_or_assert(); !construction.empty()) {
for(; first != last; ++first) {
construction.publish(reg, *first);
}
}
}
/**
* @brief Forwards variables to derived classes, if any.
* @param value A variable wrapped in an opaque container.
*/
void bind(any value) noexcept final {
auto *reg = any_cast<basic_registry_type>(&value);
owner = reg ? reg : owner;
underlying_type::bind(std::move(value));
}
private:
basic_registry_type *owner;
sigh_type construction;
sigh_type destruction;
sigh_type update;
};
} // namespace entt
#endif

View File

@ -43,7 +43,7 @@ struct basic_collector<> {
* @return The updated collector.
*/
template<typename... AllOf, typename... NoneOf>
static constexpr auto group(exclude_t<NoneOf...> = {}) noexcept {
static constexpr auto group(exclude_t<NoneOf...> = exclude_t{}) noexcept {
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>>{};
}
@ -78,7 +78,7 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
* @return The updated collector.
*/
template<typename... AllOf, typename... NoneOf>
static constexpr auto group(exclude_t<NoneOf...> = {}) noexcept {
static constexpr auto group(exclude_t<NoneOf...> = exclude_t{}) noexcept {
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>, current_type, Other...>{};
}
@ -99,7 +99,7 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule
* @return The updated collector.
*/
template<typename... AllOf, typename... NoneOf>
static constexpr auto where(exclude_t<NoneOf...> = {}) noexcept {
static constexpr auto where(exclude_t<NoneOf...> = exclude_t{}) noexcept {
using extended_type = matcher<type_list<Reject..., NoneOf...>, type_list<Require..., AllOf...>, Rule...>;
return basic_collector<extended_type, Other...>{};
}
@ -146,8 +146,7 @@ inline constexpr basic_collector<> collector{};
* * The entity currently pointed is destroyed.
*
* In all the other cases, modifying the pools of the given components in any
* way invalidates all the iterators and using them results in undefined
* behavior.
* way invalidates all the iterators.
*
* @warning
* Lifetime of an observer doesn't necessarily have to overcome that of the
@ -156,10 +155,12 @@ inline constexpr basic_collector<> collector{};
* pointers.
*
* @tparam Registry Basic registry type.
* @tparam Mask Mask type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Registry>
class basic_observer: private basic_storage<std::uint32_t, typename Registry::entity_type> {
using base_type = basic_storage<std::uint32_t, typename Registry::entity_type>;
template<typename Registry, typename Mask, typename Allocator>
class basic_observer: private basic_storage<Mask, typename Registry::entity_type, Allocator> {
using base_type = basic_storage<Mask, typename Registry::entity_type, Allocator>;
template<typename>
struct matcher_handler;
@ -193,10 +194,10 @@ class basic_observer: private basic_storage<std::uint32_t, typename Registry::en
}
static void disconnect(basic_observer &obs, Registry &reg) {
(reg.template on_destroy<Require>().disconnect(obs), ...);
(reg.template on_construct<Reject>().disconnect(obs), ...);
reg.template on_update<AnyOf>().disconnect(obs);
reg.template on_destroy<AnyOf>().disconnect(obs);
(reg.template on_destroy<Require>().disconnect(&obs), ...);
(reg.template on_construct<Reject>().disconnect(&obs), ...);
reg.template on_update<AnyOf>().disconnect(&obs);
reg.template on_destroy<AnyOf>().disconnect(&obs);
}
};
@ -239,12 +240,12 @@ class basic_observer: private basic_storage<std::uint32_t, typename Registry::en
}
static void disconnect(basic_observer &obs, Registry &reg) {
(reg.template on_destroy<Require>().disconnect(obs), ...);
(reg.template on_construct<Reject>().disconnect(obs), ...);
(reg.template on_construct<AllOf>().disconnect(obs), ...);
(reg.template on_destroy<NoneOf>().disconnect(obs), ...);
(reg.template on_destroy<AllOf>().disconnect(obs), ...);
(reg.template on_construct<NoneOf>().disconnect(obs), ...);
(reg.template on_destroy<Require>().disconnect(&obs), ...);
(reg.template on_construct<Reject>().disconnect(&obs), ...);
(reg.template on_construct<AllOf>().disconnect(&obs), ...);
(reg.template on_destroy<NoneOf>().disconnect(&obs), ...);
(reg.template on_destroy<AllOf>().disconnect(&obs), ...);
(reg.template on_construct<NoneOf>().disconnect(&obs), ...);
}
};
@ -267,15 +268,26 @@ public:
using entity_type = typename registry_type::entity_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Random access iterator type. */
using iterator = typename registry_type::base_type::iterator;
using iterator = typename registry_type::common_type::iterator;
/*! @brief Default constructor. */
basic_observer()
: release{} {}
: basic_observer{allocator_type{}} {}
/**
* @brief Constructs an empty storage with a given allocator.
* @param allocator The allocator to use.
*/
explicit basic_observer(const allocator_type &allocator)
: base_type{allocator},
release{} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_observer(const basic_observer &) = delete;
/*! @brief Default move constructor, deleted on purpose. */
basic_observer(basic_observer &&) = delete;
@ -283,16 +295,14 @@ public:
* @brief Creates an observer and connects it to a given registry.
* @tparam Matcher Types of matchers to use to initialize the observer.
* @param reg A valid reference to a registry.
* @param allocator The allocator to use.
*/
template<typename... Matcher>
basic_observer(registry_type &reg, basic_collector<Matcher...>)
: basic_observer{} {
basic_observer(registry_type &reg, basic_collector<Matcher...>, const allocator_type &allocator = allocator_type{})
: basic_observer{allocator} {
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
}
/*! @brief Default destructor. */
~basic_observer() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This observer.
@ -360,8 +370,7 @@ public:
/**
* @brief Returns an iterator to the first entity of the observer.
*
* The returned iterator points to the first entity of the observer. If the
* container is empty, the returned iterator will be equal to `end()`.
* If the observer is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first entity of the observer.
*/
@ -371,11 +380,6 @@ public:
/**
* @brief Returns an iterator that is past the last entity of the observer.
*
* The returned iterator points to the entity following the last entity of
* the observer. Attempting to dereference the returned iterator results in
* undefined behavior.
*
* @return An iterator to the entity following the last entity of the
* observer.
*/

View File

@ -126,7 +126,7 @@ class basic_organizer final {
if constexpr(std::is_same_v<Type, Registry>) {
return reg;
} else if constexpr(internal::is_view_v<Type>) {
return as_view{reg};
return static_cast<Type>(as_view{reg});
} else {
return reg.ctx().template emplace<std::remove_reference_t<Type>>();
}

File diff suppressed because it is too large Load Diff

View File

@ -104,38 +104,22 @@ private:
/**
* @brief Generic runtime view.
*
* Runtime views iterate over those entities that have at least all the given
* components in their bags. During initialization, a runtime view looks at the
* number of entities available for each component and picks up a reference to
* the smallest set of candidate entities in order to get a performance boost
* when iterate.<br/>
* Order of elements during iterations are highly dependent on the order of the
* underlying data structures. See sparse_set and its specializations for more
* details.
* Runtime views iterate over those entities that are at least in the given
* storage. During initialization, a runtime view looks at the number of
* entities available for each component and uses the smallest set in order to
* get a performance boost when iterating.
*
* @b Important
*
* Iterators aren't invalidated if:
*
* * New instances of the given components are created and assigned to entities.
* * The entity currently pointed is modified (as an example, if one of the
* given components is removed from the entity to which the iterator points).
* * New elements are added to the storage.
* * The entity currently pointed is modified (for example, components are added
* or removed from it).
* * The entity currently pointed is destroyed.
*
* In all the other cases, modifying the pools of the given components in any
* way invalidates all the iterators and using them results in undefined
* behavior.
*
* @note
* Views share references to the underlying data structures of the registry that
* generated them. Therefore any change to the entities and to the components
* made by means of the registry are immediately reflected by the views, unless
* a pool was missing when the view was built (in this case, the view won't
* have a valid reference and won't be updated accordingly).
*
* @warning
* Lifetime of a view must not overcome that of the registry that generated it.
* In any other case, attempting to use a view results in undefined behavior.
* In all other cases, modifying the storage iterated by the view in any way
* invalidates all the iterators.
*
* @tparam Type Common base type.
* @tparam Allocator Type of allocator used to manage memory and elements.
@ -154,9 +138,9 @@ public:
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Common type among all storage types. */
using base_type = Type;
using common_type = Type;
/*! @brief Bidirectional iterator type. */
using iterator = internal::runtime_view_iterator<base_type>;
using iterator = internal::runtime_view_iterator<common_type>;
/*! @brief Default constructor to use to create empty, invalid views. */
basic_runtime_view() noexcept
@ -235,7 +219,7 @@ public:
* @param base An opaque reference to a storage object.
* @return This runtime view.
*/
basic_runtime_view &iterate(base_type &base) {
basic_runtime_view &iterate(common_type &base) {
if(pools.empty() || !(base.size() < pools[0u]->size())) {
pools.push_back(&base);
} else {
@ -250,7 +234,7 @@ public:
* @param base An opaque reference to a storage object.
* @return This runtime view.
*/
basic_runtime_view &exclude(base_type &base) {
basic_runtime_view &exclude(common_type &base) {
filter.push_back(&base);
return *this;
}
@ -267,9 +251,7 @@ public:
* @brief Returns an iterator to the first entity that has the given
* components.
*
* The returned iterator points to the first entity that has the given
* components. If the view is empty, the returned iterator will be equal to
* `end()`.
* If the view is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first entity that has the given components.
*/
@ -280,11 +262,6 @@ public:
/**
* @brief Returns an iterator that is past the last entity that has the
* given components.
*
* The returned iterator points to the entity following the last entity that
* has the given components. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @return An iterator to the entity following the last entity that has the
* given components.
*/
@ -307,8 +284,7 @@ public:
* @brief Iterates entities and applies the given function object to them.
*
* The function object is invoked for each entity. It is provided only with
* the entity itself. To get the components, users can use the registry with
* which the view was built.<br/>
* the entity itself.<br/>
* The signature of the function should be equivalent to the following:
*
* @code{.cpp}

View File

@ -1,7 +1,6 @@
#ifndef ENTT_ENTITY_SNAPSHOT_HPP
#define ENTT_ENTITY_SNAPSHOT_HPP
#include <array>
#include <cstddef>
#include <iterator>
#include <tuple>
@ -11,13 +10,37 @@
#include "../config/config.h"
#include "../container/dense_map.hpp"
#include "../core/type_traits.hpp"
#include "component.hpp"
#include "entity.hpp"
#include "fwd.hpp"
#include "view.hpp"
namespace entt {
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename Registry>
void orphans(Registry &registry) {
auto view = registry.template view<typename Registry::entity_type>();
for(auto entt: view) {
if(registry.orphan(entt)) {
view.storage()->erase(entt);
}
}
}
} // namespace internal
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Utility class to create snapshots from a registry.
*
@ -30,34 +53,8 @@ namespace entt {
*/
template<typename Registry>
class basic_snapshot {
using entity_traits = entt_traits<typename Registry::entity_type>;
template<typename Component, typename Archive, typename It>
void get(Archive &archive, std::size_t sz, It first, It last) const {
const auto view = reg->template view<const Component>();
archive(typename entity_traits::entity_type(sz));
while(first != last) {
const auto entt = *(first++);
if(reg->template all_of<Component>(entt)) {
std::apply(archive, std::tuple_cat(std::make_tuple(entt), view.get(entt)));
}
}
}
template<typename... Component, typename Archive, typename It, std::size_t... Index>
void component(Archive &archive, It first, It last, std::index_sequence<Index...>) const {
std::array<std::size_t, sizeof...(Index)> size{};
auto begin = first;
while(begin != last) {
const auto entt = *(begin++);
((reg->template all_of<Component>(entt) ? ++size[Index] : 0u), ...);
}
(get<Component>(archive, size[Index], first, last), ...);
}
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
using traits_type = typename Registry::traits_type;
public:
/*! Basic registry type. */
@ -79,58 +76,96 @@ public:
basic_snapshot &operator=(basic_snapshot &&) noexcept = default;
/**
* @brief Puts aside all the entities from the underlying registry.
*
* Entities are serialized along with their versions. Destroyed entities are
* taken in consideration as well by this function.
*
* @brief Serializes all elements of a type with associated identifiers.
* @tparam Type Type of elements to serialize.
* @tparam Archive Type of output archive.
* @param archive A valid reference to an output archive.
* @param id Optional name used to map the storage within the registry.
* @return An object of this type to continue creating the snapshot.
*/
template<typename Archive>
const basic_snapshot &entities(Archive &archive) const {
const auto sz = reg->size();
template<typename Type, typename Archive>
const basic_snapshot &get(Archive &archive, const id_type id = type_hash<Type>::value()) const {
if(const auto *storage = reg->template storage<Type>(id); storage) {
archive(static_cast<typename traits_type::entity_type>(storage->size()));
archive(typename entity_traits::entity_type(sz + 1u));
archive(reg->released());
if constexpr(std::is_same_v<Type, entity_type>) {
archive(static_cast<typename traits_type::entity_type>(storage->in_use()));
for(auto first = reg->data(), last = first + sz; first != last; ++first) {
archive(*first);
for(auto first = storage->data(), last = first + storage->size(); first != last; ++first) {
archive(*first);
}
} else {
for(auto elem: storage->reach()) {
std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, elem);
}
}
} else {
archive(typename traits_type::entity_type{});
}
return *this;
}
/**
* @brief Puts aside the given components.
*
* Each instance is serialized together with the entity to which it belongs.
* Entities are serialized along with their versions.
*
* @brief Serializes all elements of a type with associated identifiers for
* the entities in a range.
* @tparam Type Type of elements to serialize.
* @tparam Archive Type of output archive.
* @tparam It Type of input iterator.
* @param archive A valid reference to an output archive.
* @param first An iterator to the first element of the range to serialize.
* @param last An iterator past the last element of the range to serialize.
* @param id Optional name used to map the storage within the registry.
* @return An object of this type to continue creating the snapshot.
*/
template<typename Type, typename Archive, typename It>
const basic_snapshot &get(Archive &archive, It first, It last, const id_type id = type_hash<Type>::value()) const {
static_assert(!std::is_same_v<Type, entity_type>, "Entity types not supported");
if(const auto *storage = reg->template storage<Type>(id); storage && !storage->empty()) {
archive(static_cast<typename traits_type::entity_type>(std::distance(first, last)));
for(; first != last; ++first) {
if(const auto entt = *first; storage->contains(entt)) {
archive(entt);
std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, storage->get_as_tuple(entt));
} else {
archive(static_cast<entity_type>(null));
}
}
} else {
archive(typename traits_type::entity_type{});
}
return *this;
}
/**
* @brief Serializes all identifiers, including those to be recycled.
* @tparam Archive Type of output archive.
* @param archive A valid reference to an output archive.
* @return An object of this type to continue creating the snapshot.
*/
template<typename Archive>
[[deprecated("use .get<Entity>(archive) instead")]] const basic_snapshot &entities(Archive &archive) const {
return get<entity_type>(archive);
}
/**
* @brief Serializes all elements of a type with associated identifiers.
* @tparam Component Types of components to serialize.
* @tparam Archive Type of output archive.
* @param archive A valid reference to an output archive.
* @return An object of this type to continue creating the snapshot.
*/
template<typename... Component, typename Archive>
const basic_snapshot &component(Archive &archive) const {
if constexpr(sizeof...(Component) == 1u) {
const auto view = reg->template view<const Component...>();
(component<Component>(archive, view.rbegin(), view.rend()), ...);
return *this;
} else {
(component<Component>(archive), ...);
return *this;
}
[[deprecated("use .get<Type>(archive) instead")]] const basic_snapshot &component(Archive &archive) const {
return (get<Component>(archive), ...);
}
/**
* @brief Puts aside the given components for the entities in a range.
*
* Each instance is serialized together with the entity to which it belongs.
* Entities are serialized along with their versions.
*
* @brief Serializes all elements of a type with associated identifiers for
* the entities in a range.
* @tparam Component Types of components to serialize.
* @tparam Archive Type of output archive.
* @tparam It Type of input iterator.
@ -140,9 +175,8 @@ public:
* @return An object of this type to continue creating the snapshot.
*/
template<typename... Component, typename Archive, typename It>
const basic_snapshot &component(Archive &archive, It first, It last) const {
component<Component...>(archive, first, last, std::index_sequence_for<Component...>{});
return *this;
[[deprecated("use .get<Type>(archive, first, last) instead")]] const basic_snapshot &component(Archive &archive, It first, It last) const {
return (get<Component>(archive, first, last), ...);
}
private:
@ -161,33 +195,8 @@ private:
*/
template<typename Registry>
class basic_snapshot_loader {
using entity_traits = entt_traits<typename Registry::entity_type>;
template<typename Component, typename Archive>
void assign(Archive &archive) const {
typename entity_traits::entity_type length{};
entity_type entt;
archive(length);
if constexpr(ignore_as_empty_v<Component>) {
while(length--) {
archive(entt);
const auto entity = reg->valid(entt) ? entt : reg->create(entt);
ENTT_ASSERT(entity == entt, "Entity not available for use");
reg->template emplace<Component>(entt);
}
} else {
Component instance;
while(length--) {
archive(entt, instance);
const auto entity = reg->valid(entt) ? entt : reg->create(entt);
ENTT_ASSERT(entity == entt, "Entity not available for use");
reg->template emplace<Component>(entt, std::move(instance));
}
}
}
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
using traits_type = typename Registry::traits_type;
public:
/*! Basic registry type. */
@ -202,7 +211,9 @@ public:
basic_snapshot_loader(registry_type &source) noexcept
: reg{&source} {
// restoring a snapshot as a whole requires a clean registry
ENTT_ASSERT(reg->empty(), "Registry must be empty");
for([[maybe_unused]] auto elem: source.storage()) {
ENTT_ASSERT(elem.second.empty(), "Registry must be empty");
}
}
/*! @brief Default move constructor. */
@ -212,48 +223,80 @@ public:
basic_snapshot_loader &operator=(basic_snapshot_loader &&) noexcept = default;
/**
* @brief Restores entities that were in use during serialization.
*
* This function restores the entities that were in use during serialization
* and gives them the versions they originally had.
*
* @brief Restores all elements of a type with associated identifiers.
* @tparam Type Type of elements to restore.
* @tparam Archive Type of input archive.
* @param archive A valid reference to an input archive.
* @param id Optional name used to map the storage within the registry.
* @return A valid loader to continue restoring data.
*/
template<typename Archive>
const basic_snapshot_loader &entities(Archive &archive) const {
typename entity_traits::entity_type length{};
template<typename Type, typename Archive>
basic_snapshot_loader &get([[maybe_unused]] Archive &archive, const id_type id = type_hash<Type>::value()) {
auto &storage = reg->template storage<Type>(id);
typename traits_type::entity_type length{};
archive(length);
std::vector<entity_type> all(length);
for(std::size_t pos{}; pos < length; ++pos) {
archive(all[pos]);
if constexpr(std::is_same_v<Type, entity_type>) {
typename traits_type::entity_type in_use{};
storage.reserve(length);
archive(in_use);
for(entity_type entity = null; length; --length) {
archive(entity);
storage.emplace(entity);
}
storage.in_use(in_use);
} else {
auto &other = reg->template storage<entity_type>();
entity_type entt{null};
while(length--) {
if(archive(entt); entt != null) {
const auto entity = other.contains(entt) ? entt : other.emplace(entt);
ENTT_ASSERT(entity == entt, "Entity not available for use");
if constexpr(Registry::template storage_for_type<Type>::traits_type::page_size == 0u) {
storage.emplace(entity);
} else {
Type elem{};
archive(elem);
storage.emplace(entity, std::move(elem));
}
}
}
}
reg->assign(++all.cbegin(), all.cend(), all[0u]);
return *this;
}
/**
* @brief Restores components and assigns them to the right entities.
* @brief Restores all identifiers, including those to be recycled.
* @tparam Archive Type of input archive.
* @param archive A valid reference to an input archive.
* @return A valid loader to continue restoring data.
*/
template<typename Archive>
[[deprecated("use .get<Entity>(archive) instead")]] basic_snapshot_loader &entities(Archive &archive) {
return get<entity_type>(archive);
}
/**
* @brief Restores all elements of a type with associated identifiers.
*
* The template parameter list must be exactly the same used during
* serialization. In the event that the entity to which the component is
* assigned doesn't exist yet, the loader will take care to create it with
* the version it originally had.
* serialization.
*
* @tparam Component Types of components to restore.
* @tparam Component Type of component to restore.
* @tparam Archive Type of input archive.
* @param archive A valid reference to an input archive.
* @return A valid loader to continue restoring data.
*/
template<typename... Component, typename Archive>
const basic_snapshot_loader &component(Archive &archive) const {
(assign<Component>(archive), ...);
return *this;
[[deprecated("use .get<Type>(archive) instead")]] basic_snapshot_loader &component(Archive &archive) {
return (get<Component>(archive), ...);
}
/**
@ -262,17 +305,12 @@ public:
* In case all the entities were serialized but only part of the components
* was saved, it could happen that some of the entities have no components
* once restored.<br/>
* This functions helps to identify and destroy those entities.
* This function helps to identify and destroy those entities.
*
* @return A valid loader to continue restoring data.
*/
const basic_snapshot_loader &orphans() const {
reg->each([this](const auto entt) {
if(reg->orphan(entt)) {
reg->release(entt);
}
});
basic_snapshot_loader &orphans() {
internal::orphans(*reg);
return *this;
}
@ -290,7 +328,7 @@ private:
* Identifiers that entities originally had are not transferred to the target.
* Instead, the loader maps remote identifiers to local ones while restoring a
* snapshot.<br/>
* An example of use is the implementation of a client-server applications with
* An example of use is the implementation of a client-server application with
* the requirement of transferring somehow parts of the representation side to
* side.
*
@ -298,29 +336,16 @@ private:
*/
template<typename Registry>
class basic_continuous_loader {
using entity_traits = entt_traits<typename Registry::entity_type>;
void destroy(typename Registry::entity_type entt) {
if(const auto it = remloc.find(entt); it == remloc.cend()) {
const auto local = reg->create();
remloc.emplace(entt, std::make_pair(local, true));
reg->destroy(local);
}
}
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
using traits_type = typename Registry::traits_type;
void restore(typename Registry::entity_type entt) {
const auto it = remloc.find(entt);
if(it == remloc.cend()) {
const auto local = reg->create();
remloc.emplace(entt, std::make_pair(local, true));
} else {
if(!reg->valid(remloc[entt].first)) {
remloc[entt].first = reg->create();
if(const auto entity = to_entity(entt); remloc.contains(entity) && remloc[entity].first == entt) {
if(!reg->valid(remloc[entity].second)) {
remloc[entity].second = reg->create();
}
// set the dirty flag
remloc[entt].second = true;
} else {
remloc.insert_or_assign(entity, std::make_pair(entt, reg->create()));
}
}
@ -369,42 +394,6 @@ class basic_continuous_loader {
}
}
template<typename Component>
void remove_if_exists() {
for(auto &&ref: remloc) {
const auto local = ref.second.first;
if(reg->valid(local)) {
reg->template remove<Component>(local);
}
}
}
template<typename Component, typename Archive, typename... Other, typename... Member>
void assign(Archive &archive, [[maybe_unused]] Member Other::*...member) {
typename entity_traits::entity_type length{};
entity_type entt;
archive(length);
if constexpr(ignore_as_empty_v<Component>) {
while(length--) {
archive(entt);
restore(entt);
reg->template emplace_or_replace<Component>(map(entt));
}
} else {
Component instance;
while(length--) {
archive(entt, instance);
(update(instance, member), ...);
restore(entt);
reg->template emplace_or_replace<Component>(map(entt), std::move(instance));
}
}
}
public:
/*! Basic registry type. */
using registry_type = Registry;
@ -416,7 +405,8 @@ public:
* @param source A valid reference to a registry.
*/
basic_continuous_loader(registry_type &source) noexcept
: reg{&source} {}
: remloc{source.get_allocator()},
reg{&source} {}
/*! @brief Default move constructor. */
basic_continuous_loader(basic_continuous_loader &&) = default;
@ -425,90 +415,142 @@ public:
basic_continuous_loader &operator=(basic_continuous_loader &&) = default;
/**
* @brief Restores entities that were in use during serialization.
* @brief Restores all elements of a type with associated identifiers.
*
* This function restores the entities that were in use during serialization
* and creates local counterparts for them if required.
* It creates local counterparts for remote elements as needed.<br/>
* Members are either data members of type entity_type or containers of
* entities. In both cases, a loader visits them and replaces entities with
* their local counterpart.
*
* @tparam Type Type of elements to restore.
* @tparam Archive Type of input archive.
* @param archive A valid reference to an input archive.
* @param id Optional name used to map the storage within the registry.
* @return A valid loader to continue restoring data.
*/
template<typename Type, typename Archive>
basic_continuous_loader &get([[maybe_unused]] Archive &archive, const id_type id = type_hash<Type>::value()) {
auto &storage = reg->template storage<Type>(id);
typename traits_type::entity_type length{};
entity_type entt{null};
archive(length);
if constexpr(std::is_same_v<Type, entity_type>) {
typename traits_type::entity_type in_use{};
storage.reserve(length);
archive(in_use);
for(std::size_t pos{}; pos < in_use; ++pos) {
archive(entt);
restore(entt);
}
for(std::size_t pos = in_use; pos < length; ++pos) {
archive(entt);
if(const auto entity = to_entity(entt); remloc.contains(entity)) {
if(reg->valid(remloc[entity].second)) {
reg->destroy(remloc[entity].second);
}
remloc.erase(entity);
}
}
} else {
for(auto &&ref: remloc) {
storage.remove(ref.second.second);
}
while(length--) {
if(archive(entt); entt != null) {
restore(entt);
if constexpr(Registry::template storage_for_type<Type>::traits_type::page_size == 0u) {
storage.emplace(map(entt));
} else {
Type elem{};
archive(elem);
storage.emplace(map(entt), std::move(elem));
}
}
}
}
return *this;
}
/**
* @brief Restores all identifiers, including those to be recycled.
*
* It creates local counterparts for remote elements as needed.
*
* @tparam Archive Type of input archive.
* @param archive A valid reference to an input archive.
* @return A non-const reference to this loader.
*/
template<typename Archive>
basic_continuous_loader &entities(Archive &archive) {
typename entity_traits::entity_type length{};
entity_type entt{};
archive(length);
// discards the head of the list of destroyed entities
archive(entt);
for(std::size_t pos{}, last = length - 1u; pos < last; ++pos) {
archive(entt);
if(const auto entity = entity_traits::to_entity(entt); entity == pos) {
restore(entt);
} else {
destroy(entt);
}
}
return *this;
[[deprecated("use .get<Entity>(archive) instead")]] basic_continuous_loader &entities(Archive &archive) {
return get<entity_type>(archive);
}
/**
* @brief Restores components and assigns them to the right entities.
* @brief Serializes all elements of a type with associated identifiers.
*
* The template parameter list must be exactly the same used during
* serialization. In the event that the entity to which the component is
* assigned doesn't exist yet, the loader will take care to create a local
* counterpart for it.<br/>
* Members can be either data members of type entity_type or containers of
* entities. In both cases, the loader will visit them and update the
* entities by replacing each one with its local counterpart.
* It creates local counterparts for remote elements as needed.<br/>
* Members are either data members of type entity_type or containers of
* entities. In both cases, a loader visits them and replaces entities with
* their local counterpart.
*
* @tparam Component Type of component to restore.
* @tparam Archive Type of input archive.
* @tparam Other Types of components to update with local counterparts.
* @tparam Member Types of members to update with their local counterparts.
* @param archive A valid reference to an input archive.
* @param member Members to update with their local counterparts.
* @return A non-const reference to this loader.
*/
template<typename... Component, typename Archive, typename... Other, typename... Member>
basic_continuous_loader &component(Archive &archive, Member Other::*...member) {
(remove_if_exists<Component>(), ...);
(assign<Component>(archive, member...), ...);
template<typename... Component, typename Archive, typename... Member, typename... Clazz>
[[deprecated("use .component<Type>(archive, members...) instead")]] basic_continuous_loader &component(Archive &archive, Member Clazz::*...member) {
([&](auto &storage) {
for(auto &&ref: remloc) {
storage.remove(ref.second.second);
}
typename traits_type::entity_type length{};
entity_type entt{null};
archive(length);
while(length--) {
if(archive(entt); entt != null) {
restore(entt);
if constexpr(std::remove_reference_t<decltype(storage)>::traits_type::page_size == 0u) {
storage.emplace(map(entt));
} else {
typename std::remove_reference_t<decltype(storage)>::value_type elem{};
archive(elem);
(update(elem, member), ...);
storage.emplace(map(entt), std::move(elem));
}
}
}
}(reg->template storage<Component>()),
...);
return *this;
}
/**
* @brief Helps to purge entities that no longer have a conterpart.
* @brief Helps to purge entities that no longer have a counterpart.
*
* Users should invoke this member function after restoring each snapshot,
* unless they know exactly what they are doing.
*
* @return A non-const reference to this loader.
*/
basic_continuous_loader &shrink() {
auto it = remloc.begin();
while(it != remloc.cend()) {
const auto local = it->second.first;
bool &dirty = it->second.second;
if(dirty) {
dirty = false;
++it;
} else {
if(reg->valid(local)) {
reg->destroy(local);
}
it = remloc.erase(it);
}
}
[[deprecated("use .get<Entity>(archive) instead")]] basic_continuous_loader &shrink() {
return *this;
}
@ -518,17 +560,12 @@ public:
* In case all the entities were serialized but only part of the components
* was saved, it could happen that some of the entities have no components
* once restored.<br/>
* This functions helps to identify and destroy those entities.
* This function helps to identify and destroy those entities.
*
* @return A non-const reference to this loader.
*/
basic_continuous_loader &orphans() {
reg->each([this](const auto entt) {
if(reg->orphan(entt)) {
reg->release(entt);
}
});
internal::orphans(*reg);
return *this;
}
@ -538,7 +575,8 @@ public:
* @return True if `entity` is managed by the loader, false otherwise.
*/
[[nodiscard]] bool contains(entity_type entt) const noexcept {
return (remloc.find(entt) != remloc.cend());
const auto it = remloc.find(to_entity(entt));
return it != remloc.cend() && it->second.first == entt;
}
/**
@ -547,18 +585,15 @@ public:
* @return The local identifier if any, the null entity otherwise.
*/
[[nodiscard]] entity_type map(entity_type entt) const noexcept {
const auto it = remloc.find(entt);
entity_type other = null;
if(it != remloc.cend()) {
other = it->second.first;
if(const auto it = remloc.find(to_entity(entt)); it != remloc.cend() && it->second.first == entt) {
return it->second.second;
}
return other;
return null;
}
private:
dense_map<entity_type, std::pair<entity_type, bool>> remloc;
dense_map<typename traits_type::entity_type, std::pair<entity_type, entity_type>> remloc;
registry_type *reg;
};

View File

@ -88,6 +88,10 @@ struct sparse_set_iterator final {
return *operator->();
}
[[nodiscard]] constexpr pointer data() const noexcept {
return packed ? packed->data() : nullptr;
}
[[nodiscard]] constexpr difference_type index() const noexcept {
return offset - 1;
}
@ -97,38 +101,38 @@ private:
difference_type offset;
};
template<typename Type, typename Other>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
template<typename Container>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return rhs.index() - lhs.index();
}
template<typename Type, typename Other>
[[nodiscard]] constexpr bool operator==(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
template<typename Container>
[[nodiscard]] constexpr bool operator==(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return lhs.index() == rhs.index();
}
template<typename Type, typename Other>
[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
template<typename Container>
[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename Type, typename Other>
[[nodiscard]] constexpr bool operator<(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
template<typename Container>
[[nodiscard]] constexpr bool operator<(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return lhs.index() > rhs.index();
}
template<typename Type, typename Other>
[[nodiscard]] constexpr bool operator>(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
return lhs.index() < rhs.index();
template<typename Container>
[[nodiscard]] constexpr bool operator>(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return rhs < lhs;
}
template<typename Type, typename Other>
[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
template<typename Container>
[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename Type, typename Other>
[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept {
template<typename Container>
[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept {
return !(lhs < rhs);
}
@ -139,14 +143,6 @@ template<typename Type, typename Other>
* @endcond
*/
/*! @brief Sparse set deletion policy. */
enum class deletion_policy : std::uint8_t {
/*! @brief Swap-and-pop deletion policy. */
swap_and_pop = 0u,
/*! @brief In-place deletion policy. */
in_place = 1u
};
/**
* @brief Basic sparse set implementation.
*
@ -154,10 +150,6 @@ enum class deletion_policy : std::uint8_t {
* Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a
* _packed_ one; one used for direct access through contiguous memory, the other
* one used to get the data through an extra level of indirection.<br/>
* This is largely used by the registry to offer users the fastest access ever
* to the components. Views and groups in general are almost entirely designed
* around sparse sets.
*
* This type of data structure is widely documented in the literature and on the
* web. This is nothing more than a customized implementation suitable for the
* purpose of the framework.
@ -167,7 +159,7 @@ enum class deletion_policy : std::uint8_t {
* no guarantees that entities are returned in the insertion order when iterate
* a sparse set. Do not make assumption on the order in any case.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Entity, typename Allocator>
@ -176,23 +168,26 @@ class basic_sparse_set {
static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
using sparse_container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
using packed_container_type = std::vector<Entity, Allocator>;
using entity_traits = entt_traits<Entity>;
[[nodiscard]] auto sparse_ptr(const Entity entt) const {
const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
const auto page = pos / entity_traits::page_size;
return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, entity_traits::page_size)) : nullptr;
const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
const auto page = pos / traits_type::page_size;
return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, traits_type::page_size)) : nullptr;
}
[[nodiscard]] auto &sparse_ref(const Entity entt) const {
ENTT_ASSERT(sparse_ptr(entt), "Invalid element");
const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
return sparse[pos / entity_traits::page_size][fast_mod(pos, entity_traits::page_size)];
const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
return sparse[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)];
}
[[nodiscard]] auto to_iterator(const Entity entt) const {
return --(end() - index(entt));
}
[[nodiscard]] auto &assure_at_least(const Entity entt) {
const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
const auto page = pos / entity_traits::page_size;
const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
const auto page = pos / traits_type::page_size;
if(!(page < sparse.size())) {
sparse.resize(page + 1u, nullptr);
@ -200,11 +195,11 @@ class basic_sparse_set {
if(!sparse[page]) {
auto page_allocator{packed.get_allocator()};
sparse[page] = alloc_traits::allocate(page_allocator, entity_traits::page_size);
std::uninitialized_fill(sparse[page], sparse[page] + entity_traits::page_size, null);
sparse[page] = alloc_traits::allocate(page_allocator, traits_type::page_size);
std::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, null);
}
auto &elem = sparse[page][fast_mod(pos, entity_traits::page_size)];
auto &elem = sparse[page][fast_mod(pos, traits_type::page_size)];
ENTT_ASSERT(elem == null, "Slot not available");
return elem;
}
@ -214,8 +209,8 @@ class basic_sparse_set {
for(auto &&page: sparse) {
if(page != nullptr) {
std::destroy(page, page + entity_traits::page_size);
alloc_traits::deallocate(page_allocator, page, entity_traits::page_size);
std::destroy(page, page + traits_type::page_size);
alloc_traits::deallocate(page_allocator, page, traits_type::page_size);
page = nullptr;
}
}
@ -226,13 +221,28 @@ private:
return nullptr;
}
virtual void swap_at(const std::size_t, const std::size_t) {}
virtual void move_element(const std::size_t, const std::size_t) {}
virtual void swap_or_move(const std::size_t, const std::size_t) {}
protected:
/*! @brief Random access iterator type. */
using basic_iterator = internal::sparse_set_iterator<packed_container_type>;
/**
* @brief Swaps two items at specific locations.
* @param lhs A position to move from.
* @param rhs The other position to move from.
*/
void swap_at(const std::size_t lhs, const std::size_t rhs) {
const auto entity = static_cast<typename traits_type::entity_type>(lhs);
const auto other = static_cast<typename traits_type::entity_type>(rhs);
sparse_ref(packed[lhs]) = traits_type::combine(other, traits_type::to_integral(packed[lhs]));
sparse_ref(packed[rhs]) = traits_type::combine(entity, traits_type::to_integral(packed[rhs]));
using std::swap;
swap(packed[lhs], packed[rhs]);
}
/**
* @brief Erases an entity from a sparse set.
* @param it An iterator to the element to pop.
@ -240,8 +250,8 @@ protected:
void swap_and_pop(const basic_iterator it) {
ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatched");
auto &self = sparse_ref(*it);
const auto entt = entity_traits::to_entity(self);
sparse_ref(packed.back()) = entity_traits::combine(entt, entity_traits::to_integral(packed.back()));
const auto entt = traits_type::to_entity(self);
sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back()));
packed[static_cast<size_type>(entt)] = packed.back();
// unnecessary but it helps to detect nasty bugs
ENTT_ASSERT((packed.back() = null, true), "");
@ -256,8 +266,8 @@ protected:
*/
void in_place_pop(const basic_iterator it) {
ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatched");
const auto entt = entity_traits::to_entity(std::exchange(sparse_ref(*it), null));
packed[static_cast<size_type>(entt)] = std::exchange(free_list, entity_traits::combine(entt, entity_traits::reserved));
const auto entt = traits_type::to_entity(std::exchange(sparse_ref(*it), null));
packed[static_cast<size_type>(entt)] = std::exchange(free_list, traits_type::combine(entt, tombstone));
}
protected:
@ -278,6 +288,23 @@ protected:
}
}
/*! @brief Erases all entities of a sparse set. */
virtual void pop_all() {
if(const auto prev = std::exchange(free_list, tombstone); prev == null) {
for(auto first = begin(); !(first.index() < 0); ++first) {
sparse_ref(*first) = null;
}
} else {
for(auto first = begin(); !(first.index() < 0); ++first) {
if(*first != tombstone) {
sparse_ref(*first) = null;
}
}
}
packed.clear();
}
/**
* @brief Assigns an entity to a sparse set.
* @param entt A valid identifier.
@ -289,25 +316,27 @@ protected:
if(auto &elem = assure_at_least(entt); free_list == null || force_back) {
packed.push_back(entt);
elem = entity_traits::combine(static_cast<typename entity_traits::entity_type>(packed.size() - 1u), entity_traits::to_integral(entt));
elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
return begin();
} else {
const auto pos = static_cast<size_type>(entity_traits::to_entity(free_list));
elem = entity_traits::combine(entity_traits::to_integral(free_list), entity_traits::to_integral(entt));
const auto pos = static_cast<size_type>(traits_type::to_entity(free_list));
elem = traits_type::combine(traits_type::to_integral(free_list), traits_type::to_integral(entt));
free_list = std::exchange(packed[pos], entt);
return --(end() - pos);
}
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Entity traits. */
using traits_type = entt_traits<Entity>;
/*! @brief Underlying entity identifier. */
using entity_type = typename entity_traits::value_type;
using entity_type = typename traits_type::value_type;
/*! @brief Underlying version type. */
using version_type = typename entity_traits::version_type;
using version_type = typename traits_type::version_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Pointer type to contained entities. */
using pointer = typename packed_container_type::const_pointer;
/*! @brief Random access iterator type. */
@ -317,7 +346,7 @@ public:
/*! @brief Reverse iterator type. */
using reverse_iterator = std::reverse_iterator<iterator>;
/*! @brief Constant reverse iterator type. */
using const_reverse_iterator = reverse_iterator;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
/*! @brief Default constructor. */
basic_sparse_set()
@ -341,14 +370,14 @@ public:
/**
* @brief Constructs an empty container with the given value type, policy
* and allocator.
* @param value Returned value type, if any.
* @param elem Returned value type, if any.
* @param pol Type of deletion policy.
* @param allocator The allocator to use (possibly default-constructed).
*/
explicit basic_sparse_set(const type_info &value, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {})
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{&value},
info{&elem},
free_list{tombstone},
mode{pol} {}
@ -465,7 +494,7 @@ public:
* @return Extent of the sparse set.
*/
[[nodiscard]] size_type extent() const noexcept {
return sparse.size() * entity_traits::page_size;
return sparse.size() * traits_type::page_size;
}
/**
@ -490,6 +519,14 @@ public:
return packed.empty();
}
/**
* @brief Checks whether a sparse set is fully packed.
* @return True if the sparse set is fully packed, false otherwise.
*/
[[nodiscard]] bool contiguous() const noexcept {
return (free_list == null);
}
/**
* @brief Direct access to the internal packed array.
* @return A pointer to the internal packed array.
@ -501,8 +538,7 @@ public:
/**
* @brief Returns an iterator to the beginning.
*
* The returned iterator points to the first entity of the internal packed
* array. If the sparse set is empty, the returned iterator will be equal to
* If the sparse set is empty, the returned iterator will be equal to
* `end()`.
*
* @return An iterator to the first entity of the sparse set.
@ -519,11 +555,6 @@ public:
/**
* @brief Returns an iterator to the end.
*
* The returned iterator points to the element following the last entity in
* a sparse set. Attempting to dereference the returned iterator results in
* undefined behavior.
*
* @return An iterator to the element following the last entity of a sparse
* set.
*/
@ -539,9 +570,8 @@ public:
/**
* @brief Returns a reverse iterator to the beginning.
*
* The returned iterator points to the first entity of the reversed internal
* packed array. If the sparse set is empty, the returned iterator will be
* equal to `rend()`.
* If the sparse set is empty, the returned iterator will be equal to
* `rend()`.
*
* @return An iterator to the first entity of the reversed internal packed
* array.
@ -557,11 +587,6 @@ public:
/**
* @brief Returns a reverse iterator to the end.
*
* The returned iterator points to the element following the last entity in
* the reversed sparse set. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @return An iterator to the element following the last entity of the
* reversed sparse set.
*/
@ -581,7 +606,7 @@ public:
* iterator otherwise.
*/
[[nodiscard]] iterator find(const entity_type entt) const noexcept {
return contains(entt) ? --(end() - index(entt)) : end();
return contains(entt) ? to_iterator(entt) : end();
}
/**
@ -591,9 +616,9 @@ public:
*/
[[nodiscard]] bool contains(const entity_type entt) const noexcept {
const auto elem = sparse_ptr(entt);
constexpr auto cap = entity_traits::to_entity(null);
constexpr auto cap = traits_type::to_entity(null);
// testing versions permits to avoid accessing the packed array
return elem && (((~cap & entity_traits::to_integral(entt)) ^ entity_traits::to_integral(*elem)) < cap);
return elem && (((~cap & traits_type::to_integral(entt)) ^ traits_type::to_integral(*elem)) < cap);
}
/**
@ -604,8 +629,8 @@ public:
*/
[[nodiscard]] version_type current(const entity_type entt) const noexcept {
const auto elem = sparse_ptr(entt);
constexpr auto fallback = entity_traits::to_version(tombstone);
return elem ? entity_traits::to_version(*elem) : fallback;
constexpr auto fallback = traits_type::to_version(tombstone);
return elem ? traits_type::to_version(*elem) : fallback;
}
/**
@ -620,7 +645,7 @@ public:
*/
[[nodiscard]] size_type index(const entity_type entt) const noexcept {
ENTT_ASSERT(contains(entt), "Set does not contain entity");
return static_cast<size_type>(entity_traits::to_entity(sparse_ref(entt)));
return static_cast<size_type>(traits_type::to_entity(sparse_ref(entt)));
}
/**
@ -652,13 +677,13 @@ public:
* @param entt A valid identifier.
* @return An opaque pointer to the element assigned to the entity, if any.
*/
[[nodiscard]] const void *get(const entity_type entt) const noexcept {
[[nodiscard]] const void *value(const entity_type entt) const noexcept {
return get_at(index(entt));
}
/*! @copydoc get */
[[nodiscard]] void *get(const entity_type entt) noexcept {
return const_cast<void *>(std::as_const(*this).get(entt));
/*! @copydoc value */
[[nodiscard]] void *value(const entity_type entt) noexcept {
return const_cast<void *>(std::as_const(*this).value(entt));
}
/**
@ -669,28 +694,12 @@ public:
* results in undefined behavior.
*
* @param entt A valid identifier.
* @param value Optional opaque value to forward to mixins, if any.
* @param elem Optional opaque element to forward to mixins, if any.
* @return Iterator pointing to the emplaced element in case of success, the
* `end()` iterator otherwise.
*/
iterator emplace(const entity_type entt, const void *value = nullptr) {
return try_emplace(entt, false, value);
}
/**
* @brief Bump the version number of an entity.
*
* @warning
* Attempting to bump the version of an entity that doesn't belong to the
* sparse set results in undefined behavior.
*
* @param entt A valid identifier.
*/
void bump(const entity_type entt) {
auto &entity = sparse_ref(entt);
ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version");
entity = entity_traits::combine(entity_traits::to_integral(entity), entity_traits::to_integral(entt));
packed[static_cast<size_type>(entity_traits::to_entity(entity))] = entt;
iterator push(const entity_type entt, const void *elem = nullptr) {
return try_emplace(entt, false, elem);
}
/**
@ -707,7 +716,7 @@ public:
* success, the `end()` iterator otherwise.
*/
template<typename It>
iterator insert(It first, It last) {
iterator push(It first, It last) {
for(auto it = first; it != last; ++it) {
try_emplace(*it, true);
}
@ -715,6 +724,24 @@ public:
return first == last ? end() : find(*first);
}
/**
* @brief Bump the version number of an entity.
*
* @warning
* Attempting to bump the version of an entity that doesn't belong to the
* sparse set results in undefined behavior.
*
* @param entt A valid identifier.
* @return The version of the given identifier.
*/
version_type bump(const entity_type entt) {
auto &entity = sparse_ref(entt);
ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version");
entity = traits_type::combine(traits_type::to_integral(entity), traits_type::to_integral(entt));
packed[static_cast<size_type>(traits_type::to_entity(entity))] = entt;
return traits_type::to_version(entt);
}
/**
* @brief Erases an entity from a sparse set.
*
@ -725,7 +752,7 @@ public:
* @param entt A valid identifier.
*/
void erase(const entity_type entt) {
const auto it = --(end() - index(entt));
const auto it = to_iterator(entt);
pop(it, it + 1u);
}
@ -769,29 +796,45 @@ public:
size_type remove(It first, It last) {
size_type count{};
for(; first != last; ++first) {
count += remove(*first);
if constexpr(std::is_same_v<It, basic_iterator>) {
while(first != last) {
while(first != last && !contains(*first)) {
++first;
}
const auto it = first;
while(first != last && contains(*first)) {
++first;
}
count += std::distance(it, first);
erase(it, first);
}
} else {
for(; first != last; ++first) {
count += remove(*first);
}
}
return count;
}
/*! @brief Removes all tombstones from the packed array of a sparse set. */
/*! @brief Removes all tombstones from a sparse set. */
void compact() {
size_type from = packed.size();
for(; from && packed[from - 1u] == tombstone; --from) {}
for(auto *it = &free_list; *it != null && from; it = std::addressof(packed[entity_traits::to_entity(*it)])) {
if(const size_type to = entity_traits::to_entity(*it); to < from) {
for(auto *it = &free_list; *it != null && from; it = std::addressof(packed[traits_type::to_entity(*it)])) {
if(const size_type to = traits_type::to_entity(*it); to < from) {
--from;
move_element(from, to);
swap_or_move(from, to);
using std::swap;
swap(packed[from], packed[to]);
packed[to] = std::exchange(packed[from], tombstone);
const auto entity = static_cast<typename traits_type::entity_type>(to);
sparse_ref(packed[to]) = traits_type::combine(entity, traits_type::to_integral(packed[to]));
const auto entity = static_cast<typename entity_traits::entity_type>(to);
sparse_ref(packed[to]) = entity_traits::combine(entity, entity_traits::to_integral(packed[to]));
*it = entity_traits::combine(static_cast<typename entity_traits::entity_type>(from), entity_traits::reserved);
*it = traits_type::combine(static_cast<typename traits_type::entity_type>(from), tombstone);
for(; from && packed[from - 1u] == tombstone; --from) {}
}
}
@ -814,21 +857,12 @@ public:
* @param rhs A valid identifier.
*/
void swap_elements(const entity_type lhs, const entity_type rhs) {
ENTT_ASSERT(contains(lhs) && contains(rhs), "Set does not contain entities");
const auto from = index(lhs);
const auto to = index(rhs);
auto &entt = sparse_ref(lhs);
auto &other = sparse_ref(rhs);
const auto from = entity_traits::to_entity(entt);
const auto to = entity_traits::to_entity(other);
// basic no-leak guarantee (with invalid state) if swapping throws
swap_at(static_cast<size_type>(from), static_cast<size_type>(to));
entt = entity_traits::combine(to, entity_traits::to_integral(packed[from]));
other = entity_traits::combine(from, entity_traits::to_integral(packed[to]));
using std::swap;
swap(packed[from], packed[to]);
// basic no-leak guarantee if swapping throws
swap_or_move(from, to);
swap_at(from, to);
}
/**
@ -876,9 +910,9 @@ public:
const auto idx = index(packed[next]);
const auto entt = packed[curr];
swap_at(next, idx);
const auto entity = static_cast<typename entity_traits::entity_type>(curr);
sparse_ref(entt) = entity_traits::combine(entity, entity_traits::to_integral(packed[curr]));
swap_or_move(next, idx);
const auto entity = static_cast<typename traits_type::entity_type>(curr);
sparse_ref(entt) = traits_type::combine(entity, traits_type::to_integral(packed[curr]));
curr = std::exchange(next, idx);
}
}
@ -906,48 +940,35 @@ public:
* @brief Sort entities according to their order in another sparse set.
*
* Entities that are part of both the sparse sets are ordered internally
* according to the order they have in `other`. All the other entities goes
* to the end of the list and there are no guarantees on their order.<br/>
* In other terms, this function can be used to impose the same order on two
* sets by using one of them as a master and the other one as a slave.
*
* Iterating the sparse set with a couple of iterators returns elements in
* the expected order after a call to `respect`. See `begin` and `end` for
* more details.
* according to the order they have in `other`.<br/>
* All the other entities goes to the end of the list and there are no
* guarantees on their order.
*
* @param other The sparse sets that imposes the order of the entities.
*/
void respect(const basic_sparse_set &other) {
void sort_as(const basic_sparse_set &other) {
compact();
const auto to = other.end();
auto from = other.begin();
for(size_type pos = packed.size() - 1; pos && from != to; ++from) {
if(contains(*from)) {
if(*from != packed[pos]) {
for(auto it = begin(); it.index() && from != to; ++from) {
if(const auto curr = *from; contains(curr)) {
if(const auto entt = *it; entt != curr) {
// basic no-leak guarantee (with invalid state) if swapping throws
swap_elements(packed[pos], *from);
swap_elements(entt, curr);
}
--pos;
++it;
}
}
}
/*! @brief Clears a sparse set. */
void clear() {
if(const auto last = end(); free_list == null) {
pop(begin(), last);
} else {
for(auto &&entity: *this) {
// tombstone filter on itself
if(const auto it = find(entity); it != last) {
pop(it, it + 1u);
}
}
}
pop_all();
// sanity check to avoid subtle issues due to storage classes
ENTT_ASSERT((compact(), size()) == 0u, "Non-empty set");
free_list = tombstone;
packed.clear();
}

View File

@ -9,7 +9,6 @@
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/compressed_pair.hpp"
#include "../core/iterator.hpp"
#include "../core/memory.hpp"
#include "../core/type_info.hpp"
@ -17,7 +16,6 @@
#include "entity.hpp"
#include "fwd.hpp"
#include "sparse_set.hpp"
#include "storage_mixin.hpp"
namespace entt {
@ -28,13 +26,12 @@ namespace entt {
namespace internal {
template<typename Container>
template<typename Container, std::size_t Size>
class storage_iterator final {
friend storage_iterator<const Container>;
friend storage_iterator<const Container, Size>;
using container_type = std::remove_const_t<Container>;
using alloc_traits = std::allocator_traits<typename container_type::allocator_type>;
using comp_traits = component_traits<std::remove_pointer_t<typename container_type::value_type>>;
using iterator_traits = std::iterator_traits<std::conditional_t<
std::is_const_v<Container>,
@ -51,12 +48,12 @@ public:
constexpr storage_iterator() noexcept = default;
constexpr storage_iterator(Container *ref, const difference_type idx) noexcept
: packed{ref},
: payload{ref},
offset{idx} {}
template<bool Const = std::is_const_v<Container>, typename = std::enable_if_t<Const>>
constexpr storage_iterator(const storage_iterator<std::remove_const_t<Container>> &other) noexcept
: storage_iterator{other.packed, other.offset} {}
constexpr storage_iterator(const storage_iterator<std::remove_const_t<Container>, Size> &other) noexcept
: storage_iterator{other.payload, other.offset} {}
constexpr storage_iterator &operator++() noexcept {
return --offset, *this;
@ -96,12 +93,12 @@ public:
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
const auto pos = index() - value;
return (*packed)[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)];
return (*payload)[pos / Size][fast_mod(pos, Size)];
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
const auto pos = index();
return (*packed)[pos / comp_traits::page_size] + fast_mod(pos, comp_traits::page_size);
return (*payload)[pos / Size] + fast_mod(pos, Size);
}
[[nodiscard]] constexpr reference operator*() const noexcept {
@ -113,42 +110,42 @@ public:
}
private:
Container *packed;
Container *payload;
difference_type offset;
};
template<typename CLhs, typename CRhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return rhs.index() - lhs.index();
}
template<typename CLhs, typename CRhs>
[[nodiscard]] constexpr bool operator==(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator==(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return lhs.index() == rhs.index();
}
template<typename CLhs, typename CRhs>
[[nodiscard]] constexpr bool operator!=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator!=(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename CLhs, typename CRhs>
[[nodiscard]] constexpr bool operator<(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator<(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return lhs.index() > rhs.index();
}
template<typename CLhs, typename CRhs>
[[nodiscard]] constexpr bool operator>(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
return lhs.index() < rhs.index();
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator>(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return rhs < lhs;
}
template<typename CLhs, typename CRhs>
[[nodiscard]] constexpr bool operator<=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator<=(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename CLhs, typename CRhs>
[[nodiscard]] constexpr bool operator>=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator>=(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return !(lhs < rhs);
}
@ -158,10 +155,11 @@ class extended_storage_iterator final {
friend class extended_storage_iterator;
public:
using iterator_type = It;
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::forward_as_tuple(*std::declval<Other>()...)));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
constexpr extended_storage_iterator()
@ -191,20 +189,24 @@ public:
return {*std::get<It>(it), *std::get<Other>(it)...};
}
template<typename... CLhs, typename... CRhs>
friend constexpr bool operator==(const extended_storage_iterator<CLhs...> &, const extended_storage_iterator<CRhs...> &) noexcept;
[[nodiscard]] constexpr iterator_type base() const noexcept {
return std::get<It>(it);
}
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator==(const extended_storage_iterator<Lhs...> &, const extended_storage_iterator<Rhs...> &) noexcept;
private:
std::tuple<It, Other...> it;
};
template<typename... CLhs, typename... CRhs>
[[nodiscard]] constexpr bool operator==(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) noexcept {
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator==(const extended_storage_iterator<Lhs...> &lhs, const extended_storage_iterator<Rhs...> &rhs) noexcept {
return std::get<0>(lhs.it) == std::get<0>(rhs.it);
}
template<typename... CLhs, typename... CRhs>
[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) noexcept {
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator<Lhs...> &lhs, const extended_storage_iterator<Rhs...> &rhs) noexcept {
return !(lhs == rhs);
}
@ -227,43 +229,43 @@ template<typename... CLhs, typename... CRhs>
* normally available for non-empty types will not be available for empty ones.
*
* @tparam Type Type of objects assigned to the entities.
* @tparam Entity A valid entity type (see entt_traits for more details).
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type, typename Entity, typename Allocator, typename>
class basic_storage: public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
using container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
using comp_traits = component_traits<Type>;
using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
using underlying_iterator = typename underlying_type::basic_iterator;
static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>);
[[nodiscard]] auto &element_at(const std::size_t pos) const {
return packed.first()[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)];
return payload[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)];
}
auto assure_at_least(const std::size_t pos) {
auto &&container = packed.first();
const auto idx = pos / comp_traits::page_size;
const auto idx = pos / traits_type::page_size;
if(!(idx < container.size())) {
auto curr = container.size();
container.resize(idx + 1u, nullptr);
if(!(idx < payload.size())) {
auto curr = payload.size();
allocator_type allocator{get_allocator()};
payload.resize(idx + 1u, nullptr);
ENTT_TRY {
for(const auto last = container.size(); curr < last; ++curr) {
container[curr] = alloc_traits::allocate(packed.second(), comp_traits::page_size);
for(const auto last = payload.size(); curr < last; ++curr) {
payload[curr] = alloc_traits::allocate(allocator, traits_type::page_size);
}
}
ENTT_CATCH {
container.resize(curr);
payload.resize(curr);
ENTT_THROW;
}
}
return container[idx] + fast_mod(pos, comp_traits::page_size);
return payload[idx] + fast_mod(pos, traits_type::page_size);
}
template<typename... Args>
@ -272,7 +274,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
ENTT_TRY {
auto elem = assure_at_least(static_cast<size_type>(it.index()));
entt::uninitialized_construct_using_allocator(to_address(elem), packed.second(), std::forward<Args>(args)...);
entt::uninitialized_construct_using_allocator(to_address(elem), get_allocator(), std::forward<Args>(args)...);
}
ENTT_CATCH {
base_type::pop(it, it + 1u);
@ -283,25 +285,24 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
}
void shrink_to_size(const std::size_t sz) {
const auto from = (sz + traits_type::page_size - 1u) / traits_type::page_size;
allocator_type allocator{get_allocator()};
for(auto pos = sz, length = base_type::size(); pos < length; ++pos) {
if constexpr(comp_traits::in_place_delete) {
if constexpr(traits_type::in_place_delete) {
if(base_type::at(pos) != tombstone) {
std::destroy_at(std::addressof(element_at(pos)));
alloc_traits::destroy(allocator, std::addressof(element_at(pos)));
}
} else {
std::destroy_at(std::addressof(element_at(pos)));
alloc_traits::destroy(allocator, std::addressof(element_at(pos)));
}
}
auto &&container = packed.first();
auto page_allocator{packed.second()};
const auto from = (sz + comp_traits::page_size - 1u) / comp_traits::page_size;
for(auto pos = from, last = container.size(); pos < last; ++pos) {
alloc_traits::deallocate(page_allocator, container[pos], comp_traits::page_size);
for(auto pos = from, last = payload.size(); pos < last; ++pos) {
alloc_traits::deallocate(allocator, payload[pos], traits_type::page_size);
}
container.resize(from);
payload.resize(from);
}
private:
@ -309,54 +310,68 @@ private:
return std::addressof(element_at(pos));
}
void swap_at([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) final {
// use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy
ENTT_ASSERT((lhs + 1u) && !is_pinned_type_v, "Pinned type");
if constexpr(!is_pinned_type_v) {
using std::swap;
swap(element_at(lhs), element_at(rhs));
}
}
void move_element([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) final {
void swap_or_move([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) override {
// use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy
ENTT_ASSERT((from + 1u) && !is_pinned_type_v, "Pinned type");
if constexpr(!is_pinned_type_v) {
auto &elem = element_at(from);
entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), packed.second(), std::move(elem));
std::destroy_at(std::addressof(elem));
if constexpr(traits_type::in_place_delete) {
if(base_type::operator[](to) == tombstone) {
allocator_type allocator{get_allocator()};
entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), allocator, std::move(elem));
alloc_traits::destroy(allocator, std::addressof(elem));
return;
}
}
using std::swap;
swap(elem, element_at(to));
}
}
protected:
/*! @brief Random access iterator type. */
using basic_iterator = typename underlying_type::basic_iterator;
/**
* @brief Erases entities from a sparse set.
* @brief Erases entities from a storage.
* @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.
*/
void pop(basic_iterator first, basic_iterator last) override {
for(; first != last; ++first) {
void pop(underlying_iterator first, underlying_iterator last) override {
for(allocator_type allocator{get_allocator()}; first != last; ++first) {
// cannot use first.index() because it would break with cross iterators
auto &elem = element_at(base_type::index(*first));
if constexpr(comp_traits::in_place_delete) {
if constexpr(traits_type::in_place_delete) {
base_type::in_place_pop(first);
std::destroy_at(std::addressof(elem));
alloc_traits::destroy(allocator, std::addressof(elem));
} else {
auto &other = element_at(base_type::size() - 1u);
// destroying on exit allows reentrant destructors
[[maybe_unused]] auto unused = std::exchange(elem, std::move(other));
std::destroy_at(std::addressof(other));
alloc_traits::destroy(allocator, std::addressof(other));
base_type::swap_and_pop(first);
}
}
}
/*! @brief Erases all entities of a storage. */
void pop_all() override {
allocator_type allocator{get_allocator()};
for(auto first = base_type::begin(); !(first.index() < 0); ++first) {
if constexpr(traits_type::in_place_delete) {
if(*first != tombstone) {
base_type::in_place_pop(first);
alloc_traits::destroy(allocator, std::addressof(element_at(static_cast<size_type>(first.index()))));
}
} else {
base_type::swap_and_pop(first);
alloc_traits::destroy(allocator, std::addressof(element_at(static_cast<size_type>(first.index()))));
}
}
}
/**
* @brief Assigns an entity to a storage.
* @param entt A valid identifier.
@ -364,7 +379,7 @@ protected:
* @param force_back Force back insertion.
* @return Iterator pointing to the emplaced element.
*/
basic_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override {
underlying_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override {
if(value) {
if constexpr(std::is_copy_constructible_v<value_type>) {
return emplace_element(entt, force_back, *static_cast<const value_type *>(value));
@ -383,22 +398,24 @@ protected:
public:
/*! @brief Base type. */
using base_type = underlying_type;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Type of the objects assigned to entities. */
using value_type = Type;
/*! @brief Component traits. */
using traits_type = component_traits<value_type>;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Pointer type to contained elements. */
using pointer = typename container_type::pointer;
/*! @brief Constant pointer type to contained elements. */
using const_pointer = typename alloc_traits::template rebind_traits<typename alloc_traits::const_pointer>::const_pointer;
/*! @brief Random access iterator type. */
using iterator = internal::storage_iterator<container_type>;
using iterator = internal::storage_iterator<container_type, traits_type::page_size>;
/*! @brief Constant random access iterator type. */
using const_iterator = internal::storage_iterator<const container_type>;
using const_iterator = internal::storage_iterator<const container_type, traits_type::page_size>;
/*! @brief Reverse iterator type. */
using reverse_iterator = std::reverse_iterator<iterator>;
/*! @brief Constant reverse iterator type. */
@ -407,6 +424,10 @@ public:
using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator, iterator>>;
/*! @brief Constant extended iterable storage proxy. */
using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator, const_iterator>>;
/*! @brief Extended reverse iterable storage proxy. */
using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator, reverse_iterator>>;
/*! @brief Constant extended reverse iterable storage proxy. */
using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator, const_reverse_iterator>>;
/*! @brief Default constructor. */
basic_storage()
@ -417,8 +438,8 @@ public:
* @param allocator The allocator to use.
*/
explicit basic_storage(const allocator_type &allocator)
: base_type{type_id<value_type>(), deletion_policy{comp_traits::in_place_delete}, allocator},
packed{container_type{allocator}, allocator} {}
: base_type{type_id<value_type>(), deletion_policy{traits_type::in_place_delete}, allocator},
payload{allocator} {}
/**
* @brief Move constructor.
@ -426,7 +447,7 @@ public:
*/
basic_storage(basic_storage &&other) noexcept
: base_type{std::move(other)},
packed{std::move(other.packed)} {}
payload{std::move(other.payload)} {}
/**
* @brief Allocator-extended move constructor.
@ -435,8 +456,8 @@ public:
*/
basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
: base_type{std::move(other), allocator},
packed{container_type{std::move(other.packed.first()), allocator}, allocator} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "Copying a storage is not allowed");
payload{std::move(other.payload), allocator} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed");
}
/*! @brief Default destructor. */
@ -450,12 +471,11 @@ public:
* @return This storage.
*/
basic_storage &operator=(basic_storage &&other) noexcept {
ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "Copying a storage is not allowed");
ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed");
shrink_to_size(0u);
base_type::operator=(std::move(other));
packed.first() = std::move(other.packed.first());
propagate_on_container_move_assignment(packed.second(), other.packed.second());
payload = std::move(other.payload);
return *this;
}
@ -465,9 +485,8 @@ public:
*/
void swap(basic_storage &other) {
using std::swap;
underlying_type::swap(other);
propagate_on_container_swap(packed.second(), other.packed.second());
swap(packed.first(), other.packed.first());
base_type::swap(other);
swap(payload, other.payload);
}
/**
@ -475,7 +494,7 @@ public:
* @return The associated allocator.
*/
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
return allocator_type{packed.second()};
return payload.get_allocator();
}
/**
@ -499,7 +518,7 @@ public:
* @return Capacity of the storage.
*/
[[nodiscard]] size_type capacity() const noexcept override {
return packed.first().size() * comp_traits::page_size;
return payload.size() * traits_type::page_size;
}
/*! @brief Requests the removal of unused capacity. */
@ -513,25 +532,24 @@ public:
* @return A pointer to the array of objects.
*/
[[nodiscard]] const_pointer raw() const noexcept {
return packed.first().data();
return payload.data();
}
/*! @copydoc raw */
[[nodiscard]] pointer raw() noexcept {
return packed.first().data();
return payload.data();
}
/**
* @brief Returns an iterator to the beginning.
*
* The returned iterator points to the first instance of the internal array.
* If the storage is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first instance of the internal array.
*/
[[nodiscard]] const_iterator cbegin() const noexcept {
const auto pos = static_cast<typename iterator::difference_type>(base_type::size());
return const_iterator{&packed.first(), pos};
return const_iterator{&payload, pos};
}
/*! @copydoc cbegin */
@ -542,21 +560,16 @@ public:
/*! @copydoc begin */
[[nodiscard]] iterator begin() noexcept {
const auto pos = static_cast<typename iterator::difference_type>(base_type::size());
return iterator{&packed.first(), pos};
return iterator{&payload, pos};
}
/**
* @brief Returns an iterator to the end.
*
* The returned iterator points to the element following the last instance
* of the internal array. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @return An iterator to the element following the last instance of the
* internal array.
*/
[[nodiscard]] const_iterator cend() const noexcept {
return const_iterator{&packed.first(), {}};
return const_iterator{&payload, {}};
}
/*! @copydoc cend */
@ -566,15 +579,13 @@ public:
/*! @copydoc end */
[[nodiscard]] iterator end() noexcept {
return iterator{&packed.first(), {}};
return iterator{&payload, {}};
}
/**
* @brief Returns a reverse iterator to the beginning.
*
* The returned iterator points to the first instance of the reversed
* internal array. If the storage is empty, the returned iterator will be
* equal to `rend()`.
* If the storage is empty, the returned iterator will be equal to `rend()`.
*
* @return An iterator to the first instance of the reversed internal array.
*/
@ -594,11 +605,6 @@ public:
/**
* @brief Returns a reverse iterator to the end.
*
* The returned iterator points to the element following the last instance
* of the reversed internal array. Attempting to dereference the returned
* iterator results in undefined behavior.
*
* @return An iterator to the element following the last instance of the
* reversed internal array.
*/
@ -663,7 +669,7 @@ public:
*/
template<typename... Args>
value_type &emplace(const entity_type entt, Args &&...args) {
if constexpr(std::is_aggregate_v<value_type>) {
if constexpr(std::is_aggregate_v<value_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<value_type>)) {
const auto it = emplace_element(entt, false, Type{std::forward<Args>(args)...});
return element_at(static_cast<size_type>(it.index()));
} else {
@ -699,12 +705,15 @@ public:
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param value An instance of the object to construct.
* @return Iterator pointing to the last element inserted, if any.
*/
template<typename It>
void insert(It first, It last, const value_type &value = {}) {
iterator insert(It first, It last, const value_type &value = {}) {
for(; first != last; ++first) {
emplace_element(*first, true, value);
}
return begin();
}
/**
@ -718,12 +727,15 @@ public:
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param from An iterator to the first element of the range of objects.
* @return Iterator pointing to the first element inserted, if any.
*/
template<typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, value_type>>>
void insert(EIt first, EIt last, CIt from) {
iterator insert(EIt first, EIt last, CIt from) {
for(; first != last; ++first, ++from) {
emplace_element(*first, true, *from);
}
return begin();
}
/**
@ -743,34 +755,54 @@ public:
return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}};
}
/**
* @brief Returns a reverse iterable object to use to _visit_ a storage.
*
* @sa each
*
* @return A reverse iterable object to use to _visit_ the storage.
*/
[[nodiscard]] reverse_iterable reach() noexcept {
return {internal::extended_storage_iterator{base_type::rbegin(), rbegin()}, internal::extended_storage_iterator{base_type::rend(), rend()}};
}
/*! @copydoc reach */
[[nodiscard]] const_reverse_iterable reach() const noexcept {
return {internal::extended_storage_iterator{base_type::crbegin(), crbegin()}, internal::extended_storage_iterator{base_type::crend(), crend()}};
}
private:
compressed_pair<container_type, allocator_type> packed;
container_type payload;
};
/*! @copydoc basic_storage */
template<typename Type, typename Entity, typename Allocator>
class basic_storage<Type, Entity, Allocator, std::enable_if_t<ignore_as_empty_v<Type>>>
class basic_storage<Type, Entity, Allocator, std::enable_if_t<component_traits<Type>::page_size == 0u>>
: public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
using comp_traits = component_traits<Type>;
public:
/*! @brief Base type. */
using base_type = underlying_type;
/*! @brief Allocator type. */
using allocator_type = Allocator;
using base_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
/*! @brief Type of the objects assigned to entities. */
using value_type = Type;
/*! @brief Component traits. */
using traits_type = component_traits<value_type>;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Extended iterable storage proxy. */
using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator>>;
/*! @brief Constant extended iterable storage proxy. */
using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator>>;
/*! @brief Extended reverse iterable storage proxy. */
using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator>>;
/*! @brief Constant extended reverse iterable storage proxy. */
using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator>>;
/*! @brief Default constructor. */
basic_storage()
@ -781,7 +813,7 @@ public:
* @param allocator The allocator to use.
*/
explicit basic_storage(const allocator_type &allocator)
: base_type{type_id<value_type>(), deletion_policy{comp_traits::in_place_delete}, allocator} {}
: base_type{type_id<value_type>(), deletion_policy{traits_type::in_place_delete}, allocator} {}
/**
* @brief Move constructor.
@ -896,6 +928,325 @@ public:
[[nodiscard]] const_iterable each() const noexcept {
return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}};
}
/**
* @brief Returns a reverse iterable object to use to _visit_ a storage.
*
* @sa each
*
* @return A reverse iterable object to use to _visit_ the storage.
*/
[[nodiscard]] reverse_iterable reach() noexcept {
return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend()}};
}
/*! @copydoc reach */
[[nodiscard]] const_reverse_iterable reach() const noexcept {
return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend()}};
}
};
/**
* @brief Swap-only entity storage specialization.
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Entity, typename Allocator>
class basic_storage<Entity, Entity, Allocator>
: public basic_sparse_set<Entity, Allocator> {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
using underlying_iterator = typename underlying_type::basic_iterator;
using local_traits_type = entt_traits<Entity>;
auto entity_at(const std::size_t pos) const noexcept {
ENTT_ASSERT(pos < local_traits_type::to_entity(null), "Invalid element");
return local_traits_type::combine(static_cast<typename local_traits_type::entity_type>(pos), {});
}
private:
void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) override {
ENTT_ASSERT(((lhs < length) + (rhs < length)) != 1u, "Cross swapping is not supported");
}
protected:
/**
* @brief Erases entities from a storage.
* @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.
*/
void pop(underlying_iterator first, underlying_iterator last) override {
for(; first != last; ++first) {
if(const auto pos = base_type::index(*first); pos < length) {
base_type::bump(local_traits_type::next(*first));
if(pos != --length) {
base_type::swap_at(pos, length);
}
}
}
}
/*! @brief Erases all entities of a sparse set. */
void pop_all() override {
length = 0u;
base_type::pop_all();
}
/**
* @brief Assigns an entity to a storage.
* @param hint A valid identifier.
* @return Iterator pointing to the emplaced element.
*/
underlying_iterator try_emplace(const Entity hint, const bool, const void *) override {
return base_type::find(emplace(hint));
}
public:
/*! @brief Base type. */
using base_type = basic_sparse_set<Entity, Allocator>;
/*! @brief Type of the objects assigned to entities. */
using value_type = Entity;
/*! @brief Component traits. */
using traits_type = component_traits<value_type>;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Extended iterable storage proxy. */
using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator>>;
/*! @brief Constant extended iterable storage proxy. */
using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator>>;
/*! @brief Extended reverse iterable storage proxy. */
using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator>>;
/*! @brief Constant extended reverse iterable storage proxy. */
using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator>>;
/*! @brief Default constructor. */
basic_storage()
: basic_storage{allocator_type{}} {
}
/**
* @brief Constructs an empty container with a given allocator.
* @param allocator The allocator to use.
*/
explicit basic_storage(const allocator_type &allocator)
: base_type{type_id<value_type>(), deletion_policy::swap_and_pop, allocator},
length{} {}
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
basic_storage(basic_storage &&other) noexcept
: base_type{std::move(other)},
length{std::exchange(other.length, size_type{})} {}
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
: base_type{std::move(other), allocator},
length{std::exchange(other.length, size_type{})} {}
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This storage.
*/
basic_storage &operator=(basic_storage &&other) noexcept {
base_type::operator=(std::move(other));
length = std::exchange(other.length, size_type{});
return *this;
}
/**
* @brief Returns the object assigned to an entity, that is `void`.
*
* @warning
* Attempting to use an entity that doesn't belong to the storage results in
* undefined behavior.
*
* @param entt A valid identifier.
*/
void get([[maybe_unused]] const entity_type entt) const noexcept {
ENTT_ASSERT(base_type::index(entt) < length, "The requested entity is not a live one");
}
/**
* @brief Returns an empty tuple.
*
* @warning
* Attempting to use an entity that doesn't belong to the storage results in
* undefined behavior.
*
* @param entt A valid identifier.
* @return Returns an empty tuple.
*/
[[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept {
ENTT_ASSERT(base_type::index(entt) < length, "The requested entity is not a live one");
return std::tuple{};
}
/**
* @brief Exchanges the contents with those of a given storage.
* @param other Storage to exchange the content with.
*/
void swap(basic_storage &other) {
using std::swap;
base_type::swap(other);
swap(length, other.length);
}
/**
* @brief Creates a new identifier or recycles a destroyed one.
* @return A valid identifier.
*/
entity_type emplace() {
if(length == base_type::size()) {
return *base_type::try_emplace(entity_at(length++), true);
}
return base_type::operator[](length++);
}
/**
* @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.
*/
entity_type emplace(const entity_type hint) {
if(hint == null || hint == tombstone) {
return emplace();
} else if(const auto curr = local_traits_type::construct(local_traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone) {
const auto pos = static_cast<size_type>(local_traits_type::to_entity(hint));
while(!(pos < base_type::size())) {
base_type::try_emplace(entity_at(base_type::size()), true);
}
base_type::swap_at(pos, length++);
} else if(const auto idx = base_type::index(curr); idx < length) {
return emplace();
} else {
base_type::swap_at(idx, length++);
}
base_type::bump(hint);
return hint;
}
/**
* @brief Updates a given identifier.
* @tparam Func Types of the function objects to invoke.
* @param entt A valid identifier.
* @param func Valid function objects.
*/
template<typename... Func>
void patch([[maybe_unused]] const entity_type entt, Func &&...func) {
ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
(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>
void insert(It first, It last) {
for(const auto sz = base_type::size(); first != last && length != sz; ++first, ++length) {
*first = base_type::operator[](length);
}
for(; first != last; ++first) {
*first = *base_type::try_emplace(entity_at(length++), true);
}
}
/**
* @brief Makes all elements in a range contiguous.
* @tparam It Type of forward iterator.
* @param first An iterator to the first element of the range to pack.
* @param last An iterator past the last element of the range to pack.
* @return The number of elements within the newly created range.
*/
template<typename It>
size_type pack(It first, It last) {
size_type len = length;
for(; first != last; ++first, --len) {
const auto pos = base_type::index(*first);
ENTT_ASSERT(pos < length, "Invalid element");
base_type::swap_at(pos, static_cast<size_type>(len - 1u));
}
return (length - len);
}
/**
* @brief Returns the number of elements considered still in use.
* @return The number of elements considered still in use.
*/
[[nodiscard]] size_type in_use() const noexcept {
return length;
}
/**
* @brief Sets the number of elements considered still in use.
* @param len The number of elements considered still in use.
*/
void in_use(const size_type len) noexcept {
ENTT_ASSERT(!(len > base_type::size()), "Invalid length");
length = len;
}
/**
* @brief Returns an iterable object to use to _visit_ a storage.
*
* The iterable object returns a tuple that contains the current entity.
*
* @return An iterable object to use to _visit_ the storage.
*/
[[nodiscard]] iterable each() noexcept {
return {internal::extended_storage_iterator{base_type::end() - length}, internal::extended_storage_iterator{base_type::end()}};
}
/*! @copydoc each */
[[nodiscard]] const_iterable each() const noexcept {
return {internal::extended_storage_iterator{base_type::cend() - length}, internal::extended_storage_iterator{base_type::cend()}};
}
/**
* @brief Returns a reverse iterable object to use to _visit_ a storage.
*
* @sa each
*
* @return A reverse iterable object to use to _visit_ the storage.
*/
[[nodiscard]] reverse_iterable reach() noexcept {
return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rbegin() + length}};
}
/*! @copydoc reach */
[[nodiscard]] const_reverse_iterable reach() const noexcept {
return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crbegin() + length}};
}
private:
size_type length;
};
} // namespace entt

View File

@ -1,236 +0,0 @@
#ifndef ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP
#define ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP
#include <utility>
#include "../config/config.h"
#include "../core/any.hpp"
#include "../signal/sigh.hpp"
#include "fwd.hpp"
namespace entt {
/**
* @brief Mixin type used to add signal support to storage types.
*
* The function type of a listener is equivalent to:
*
* @code{.cpp}
* void(basic_registry<entity_type> &, entity_type);
* @endcode
*
* This applies to all signals made available.
*
* @tparam Type The type of the underlying storage.
*/
template<typename Type>
class sigh_storage_mixin final: public Type {
using basic_registry_type = basic_registry<typename Type::entity_type, typename Type::base_type::allocator_type>;
using sigh_type = sigh<void(basic_registry_type &, const typename Type::entity_type), typename Type::allocator_type>;
using basic_iterator = typename Type::basic_iterator;
void pop(basic_iterator first, basic_iterator last) override {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
for(; first != last; ++first) {
const auto entt = *first;
destruction.publish(*owner, entt);
const auto it = Type::find(entt);
Type::pop(it, it + 1u);
}
}
basic_iterator try_emplace(const typename basic_registry_type::entity_type entt, const bool force_back, const void *value) final {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
Type::try_emplace(entt, force_back, value);
construction.publish(*owner, entt);
return Type::find(entt);
}
public:
/*! @brief Allocator type. */
using allocator_type = typename Type::allocator_type;
/*! @brief Underlying entity identifier. */
using entity_type = typename Type::entity_type;
/*! @brief Expected registry type. */
using registry_type = basic_registry_type;
/*! @brief Default constructor. */
sigh_storage_mixin()
: sigh_storage_mixin{allocator_type{}} {}
/**
* @brief Constructs an empty storage with a given allocator.
* @param allocator The allocator to use.
*/
explicit sigh_storage_mixin(const allocator_type &allocator)
: Type{allocator},
owner{},
construction{allocator},
destruction{allocator},
update{allocator} {}
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
sigh_storage_mixin(sigh_storage_mixin &&other) noexcept
: Type{std::move(other)},
owner{other.owner},
construction{std::move(other.construction)},
destruction{std::move(other.destruction)},
update{std::move(other.update)} {}
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
sigh_storage_mixin(sigh_storage_mixin &&other, const allocator_type &allocator) noexcept
: Type{std::move(other), allocator},
owner{other.owner},
construction{std::move(other.construction), allocator},
destruction{std::move(other.destruction), allocator},
update{std::move(other.update), allocator} {}
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This storage.
*/
sigh_storage_mixin &operator=(sigh_storage_mixin &&other) noexcept {
Type::operator=(std::move(other));
owner = other.owner;
construction = std::move(other.construction);
destruction = std::move(other.destruction);
update = std::move(other.update);
return *this;
}
/**
* @brief Exchanges the contents with those of a given storage.
* @param other Storage to exchange the content with.
*/
void swap(sigh_storage_mixin &other) {
using std::swap;
Type::swap(other);
swap(owner, other.owner);
swap(construction, other.construction);
swap(destruction, other.destruction);
swap(update, other.update);
}
/**
* @brief Returns a sink object.
*
* The sink returned by this function can be used to receive notifications
* whenever a new instance is created and assigned to an entity.<br/>
* Listeners are invoked after the object has been assigned to the entity.
*
* @sa sink
*
* @return A temporary sink object.
*/
[[nodiscard]] auto on_construct() noexcept {
return sink{construction};
}
/**
* @brief Returns a sink object.
*
* The sink returned by this function can be used to receive notifications
* whenever an instance is explicitly updated.<br/>
* Listeners are invoked after the object has been updated.
*
* @sa sink
*
* @return A temporary sink object.
*/
[[nodiscard]] auto on_update() noexcept {
return sink{update};
}
/**
* @brief Returns a sink object.
*
* The sink returned by this function can be used to receive notifications
* whenever an instance is removed from an entity and thus destroyed.<br/>
* Listeners are invoked before the object has been removed from the entity.
*
* @sa sink
*
* @return A temporary sink object.
*/
[[nodiscard]] auto on_destroy() noexcept {
return sink{destruction};
}
/**
* @brief Assigns entities to a storage.
* @tparam Args Types of arguments to use to construct the object.
* @param entt A valid identifier.
* @param args Parameters to use to initialize the object.
* @return A reference to the newly created object.
*/
template<typename... Args>
decltype(auto) emplace(const entity_type entt, Args &&...args) {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
Type::emplace(entt, std::forward<Args>(args)...);
construction.publish(*owner, entt);
return this->get(entt);
}
/**
* @brief Patches the given instance for an entity.
* @tparam Func Types of the function objects to invoke.
* @param entt A valid identifier.
* @param func Valid function objects.
* @return A reference to the patched instance.
*/
template<typename... Func>
decltype(auto) patch(const entity_type entt, Func &&...func) {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
Type::patch(entt, std::forward<Func>(func)...);
update.publish(*owner, entt);
return this->get(entt);
}
/**
* @brief Assigns entities to a storage.
* @tparam It Type of input iterator.
* @tparam Args Types of arguments to use to construct the objects assigned
* to the entities.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param args Parameters to use to initialize the objects assigned to the
* entities.
*/
template<typename It, typename... Args>
void insert(It first, It last, Args &&...args) {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
Type::insert(first, last, std::forward<Args>(args)...);
for(auto it = construction.empty() ? last : first; it != last; ++it) {
construction.publish(*owner, *it);
}
}
/**
* @brief Forwards variables to derived classes, if any.
* @param value A variable wrapped in an opaque container.
*/
void bind(any value) noexcept final {
auto *reg = any_cast<basic_registry_type>(&value);
owner = reg ? reg : owner;
Type::bind(std::move(value));
}
private:
basic_registry_type *owner;
sigh_type construction;
sigh_type destruction;
sigh_type update;
};
} // namespace entt
#endif

View File

@ -9,11 +9,8 @@
#include "../config/config.h"
#include "../core/iterator.hpp"
#include "../core/type_traits.hpp"
#include "component.hpp"
#include "entity.hpp"
#include "fwd.hpp"
#include "sparse_set.hpp"
#include "storage.hpp"
namespace entt {
@ -24,6 +21,24 @@ namespace entt {
namespace internal {
template<typename... Args, typename Type, std::size_t N>
[[nodiscard]] auto filter_as_tuple(const std::array<const Type *, N> &filter) noexcept {
return std::apply([](const auto *...curr) { return std::make_tuple(static_cast<Args *>(const_cast<constness_as_t<Type, Args> *>(curr))...); }, filter);
}
template<typename Type, std::size_t N>
[[nodiscard]] auto none_of(const std::array<const Type *, N> &filter, const typename Type::entity_type entt) noexcept {
return std::apply([entt](const auto *...curr) { return (!(curr && curr->contains(entt)) && ...); }, filter);
}
template<typename... Get, typename... Exclude, std::size_t... Index>
[[nodiscard]] auto view_pack(const std::tuple<Get *...> value, const std::tuple<Exclude *...> excl, std::index_sequence<Index...>) {
const auto pools = std::tuple_cat(value, excl);
basic_view<get_t<Get...>, exclude_t<Exclude...>> elem{};
(((std::get<Index>(pools) != nullptr) ? elem.template storage<Index>(*std::get<Index>(pools)) : void()), ...);
return elem;
}
template<typename Type, std::size_t Get, std::size_t Exclude>
class view_iterator final {
using iterator_type = typename Type::const_iterator;
@ -31,7 +46,7 @@ class view_iterator final {
[[nodiscard]] bool valid() const noexcept {
return ((Get != 0u) || (*it != tombstone))
&& std::apply([entt = *it](const auto *...curr) { return (curr->contains(entt) && ...); }, pools)
&& std::apply([entt = *it](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter);
&& none_of(filter, *it);
}
public:
@ -47,11 +62,11 @@ public:
pools{},
filter{} {}
view_iterator(iterator_type curr, iterator_type to, std::array<const Type *, Get> all_of, std::array<const Type *, Exclude> none_of) noexcept
view_iterator(iterator_type curr, iterator_type to, std::array<const Type *, Get> value, std::array<const Type *, Exclude> excl) noexcept
: it{curr},
last{to},
pools{all_of},
filter{none_of} {
pools{value},
filter{excl} {
while(it != last && !valid()) {
++it;
}
@ -97,6 +112,7 @@ template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs>
template<typename It, typename... Type>
struct extended_view_iterator final {
using iterator_type = It;
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::declval<Type>().get_as_tuple({})...));
using pointer = input_iterator_pointer<value_type>;
@ -107,9 +123,9 @@ struct extended_view_iterator final {
: it{},
pools{} {}
extended_view_iterator(It from, std::tuple<Type *...> storage)
extended_view_iterator(It from, std::tuple<Type *...> value)
: it{from},
pools{storage} {}
pools{value} {}
extended_view_iterator &operator++() noexcept {
return ++it, *this;
@ -128,6 +144,10 @@ struct extended_view_iterator final {
return operator*();
}
[[nodiscard]] constexpr iterator_type base() const noexcept {
return it;
}
template<typename... Lhs, typename... Rhs>
friend bool constexpr operator==(const extended_view_iterator<Lhs...> &, const extended_view_iterator<Rhs...> &) noexcept;
@ -179,31 +199,33 @@ class basic_view;
* or removed from it).
* * The entity currently pointed is destroyed.
*
* In all other cases, modifying the pools iterated by the view in any way
* invalidates all the iterators and using them results in undefined behavior.
* In all other cases, modifying the storage iterated by the view in any way
* invalidates all the iterators.
*
* @tparam Get Types of storage iterated by the view.
* @tparam Exclude Types of storage used to filter the view.
*/
template<typename... Get, typename... Exclude>
class basic_view<get_t<Get...>, exclude_t<Exclude...>> {
using underlying_type = std::common_type_t<typename Get::entity_type..., typename Exclude::entity_type...>;
using basic_common_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>;
static constexpr auto offset = sizeof...(Get);
using base_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>;
using underlying_type = typename base_type::entity_type;
template<typename, typename, typename>
friend class basic_view;
template<typename Type>
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::value_type...>>;
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::value_type..., typename Exclude::value_type...>>;
[[nodiscard]] auto opaque_check_set() const noexcept {
std::array<const base_type *, sizeof...(Get) - 1u> other{};
std::array<const common_type *, sizeof...(Get) - 1u> other{};
std::apply([&other, pos = 0u, view = view](const auto *...curr) mutable { ((curr == view ? void() : void(other[pos++] = curr)), ...); }, pools);
return other;
}
[[nodiscard]] auto filter_as_array() const noexcept {
return std::apply([](const auto *...curr) { return std::array<const base_type *, sizeof...(Exclude)>{curr...}; }, filter);
void unchecked_refresh() noexcept {
view = std::get<0>(pools);
std::apply([this](auto *, auto *...other) { ((this->view = other->size() < this->view->size() ? other : this->view), ...); }, pools);
}
template<std::size_t Curr, std::size_t Other, typename... Args>
@ -211,18 +233,14 @@ class basic_view<get_t<Get...>, exclude_t<Exclude...>> {
if constexpr(Curr == Other) {
return std::forward_as_tuple(std::get<Args>(curr)...);
} else {
return storage<Other>().get_as_tuple(std::get<0>(curr));
return std::get<Other>(pools)->get_as_tuple(std::get<0>(curr));
}
}
[[nodiscard]] auto reject(const underlying_type entt) const noexcept {
return std::apply([entt](const auto *...curr) { return (curr->contains(entt) || ...); }, filter);
}
template<std::size_t Curr, typename Func, std::size_t... Index>
void each(Func &func, std::index_sequence<Index...>) const {
for(const auto curr: storage<Curr>().each()) {
if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && ((Curr == Index || storage<Index>().contains(entt)) && ...) && !reject(entt)) {
for(const auto curr: std::get<Curr>(pools)->each()) {
if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && ((Curr == Index || std::get<Index>(pools)->contains(entt)) && ...) && internal::none_of(filter, entt)) {
if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_view>().get({})))>) {
std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get<Curr, Index>(curr)...));
} else {
@ -234,7 +252,7 @@ class basic_view<get_t<Get...>, exclude_t<Exclude...>> {
template<typename Func, std::size_t... Index>
void pick_and_each(Func &func, std::index_sequence<Index...> seq) const {
((&storage<Index>() == view ? each<Index>(func, seq) : void()), ...);
((std::get<Index>(pools) == view ? each<Index>(func, seq) : void()), ...);
}
public:
@ -243,9 +261,9 @@ public:
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Common type among all storage types. */
using base_type = basic_common_type;
using common_type = base_type;
/*! @brief Bidirectional iterator type. */
using iterator = internal::view_iterator<base_type, sizeof...(Get) - 1u, sizeof...(Exclude)>;
using iterator = internal::view_iterator<common_type, sizeof...(Get) - 1u, sizeof...(Exclude)>;
/*! @brief Iterable view type. */
using iterable = iterable_adaptor<internal::extended_view_iterator<iterator, Get...>>;
@ -258,12 +276,14 @@ public:
/**
* @brief Constructs a multi-type view from a set of storage classes.
* @param value The storage for the types to iterate.
* @param exclude The storage for the types used to filter the view.
* @param excl The storage for the types used to filter the view.
*/
basic_view(Get &...value, Exclude &...exclude) noexcept
basic_view(Get &...value, Exclude &...excl) noexcept
: pools{&value...},
filter{&exclude...},
view{[](const base_type *first, const auto *...other) { ((first = other->size() < first->size() ? other : first), ...); return first; }(&value...)} {}
filter{&excl...},
view{} {
unchecked_refresh();
}
/**
* @brief Constructs a multi-type view from a set of storage classes.
@ -271,66 +291,91 @@ public:
* @param excl The storage for the types used to filter the view.
*/
basic_view(std::tuple<Get &...> value, std::tuple<Exclude &...> excl = {}) noexcept
: pools{std::apply([](auto &...curr) { return std::make_tuple(&curr...); }, value)},
filter{std::apply([](auto &...curr) { return std::make_tuple(&curr...); }, excl)},
view{std::apply([](const base_type *first, const auto *...other) { ((first = other->size() < first->size() ? other : first), ...); return first; }, pools)} {}
: basic_view{std::make_from_tuple<basic_view>(std::tuple_cat(value, excl))} {}
/**
* @brief Creates a new view driven by a given component in its iterations.
* @tparam Type Type of component used to drive the iteration.
* @return A new view driven by the given component in its iterations.
* @brief Forces a view to use a given component to drive iterations
* @tparam Type Type of component to use to drive iterations.
*/
template<typename Type>
[[nodiscard]] basic_view use() const noexcept {
return use<index_of<Type>>();
void use() noexcept {
use<index_of<Type>>();
}
/**
* @brief Creates a new view driven by a given component in its iterations.
* @tparam Index Index of the component used to drive the iteration.
* @return A new view driven by the given component in its iterations.
* @brief Forces a view to use a given component to drive iterations
* @tparam Index Index of the component to use to drive iterations.
*/
template<std::size_t Index>
[[nodiscard]] basic_view use() const noexcept {
basic_view other{*this};
other.view = &storage<Index>();
return other;
void use() noexcept {
if(view) {
view = std::get<Index>(pools);
}
}
/*! @brief Updates the internal leading view if required. */
void refresh() noexcept {
if(view || std::apply([](const auto *...curr) { return ((curr != nullptr) && ...); }, pools)) {
unchecked_refresh();
}
}
/**
* @brief Updates the internal leading view if required.
* @return A newly created and internally optimized view.
*/
[[nodiscard]] basic_view refresh() const noexcept {
return std::apply([](auto *...elem) { return basic_view{*elem...}; }, std::tuple_cat(pools, filter));
}
/**
* @brief Returns the leading storage of a view.
* @brief Returns the leading storage of a view, if any.
* @return The leading storage of the view.
*/
[[nodiscard]] const base_type &handle() const noexcept {
return *view;
[[nodiscard]] const common_type *handle() const noexcept {
return view;
}
/**
* @brief Returns the storage for a given component type.
* @tparam Comp Type of component of which to return the storage.
* @brief Returns the storage for a given component type, if any.
* @tparam Type Type of component of which to return the storage.
* @return The storage for the given component type.
*/
template<typename Type>
[[nodiscard]] decltype(auto) storage() const noexcept {
[[nodiscard]] auto *storage() const noexcept {
return storage<index_of<Type>>();
}
/**
* @brief Returns the storage for a given index.
* @brief Returns the storage for a given index, if any.
* @tparam Index Index of the storage to return.
* @return The storage for the given index.
*/
template<std::size_t Index>
[[nodiscard]] decltype(auto) storage() const noexcept {
return *std::get<Index>(pools);
[[nodiscard]] auto *storage() const noexcept {
if constexpr(Index < offset) {
return std::get<Index>(pools);
} else {
return std::get<Index - offset>(internal::filter_as_tuple<Exclude...>(filter));
}
}
/**
* @brief Assigns a storage to a view.
* @tparam Type Type of storage to assign to the view.
* @param elem A storage to assign to the view.
*/
template<typename Type>
void storage(Type &elem) noexcept {
storage<index_of<typename Type::value_type>>(elem);
}
/**
* @brief Assigns a storage to a view.
* @tparam Index Index of the storage to assign to the view.
* @tparam Type Type of storage to assign to the view.
* @param elem A storage to assign to the view.
*/
template<std::size_t Index, typename Type>
void storage(Type &elem) noexcept {
if constexpr(Index < offset) {
std::get<Index>(pools) = &elem;
refresh();
} else {
std::get<Index - offset>(filter) = &elem;
}
}
/**
@ -338,32 +383,26 @@ public:
* @return Estimated number of entities iterated by the view.
*/
[[nodiscard]] size_type size_hint() const noexcept {
return view->size();
return view ? view->size() : size_type{};
}
/**
* @brief Returns an iterator to the first entity of the view.
*
* The returned iterator points to the first entity of the view. If the view
* is empty, the returned iterator will be equal to `end()`.
* If the view is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first entity of the view.
*/
[[nodiscard]] iterator begin() const noexcept {
return iterator{view->begin(), view->end(), opaque_check_set(), filter_as_array()};
return view ? iterator{view->begin(), view->end(), opaque_check_set(), filter} : iterator{};
}
/**
* @brief Returns an iterator that is past the last entity of the view.
*
* The returned iterator points to the entity following the last entity of
* the view. Attempting to dereference the returned iterator results in
* undefined behavior.
*
* @return An iterator to the entity following the last entity of the view.
*/
[[nodiscard]] iterator end() const noexcept {
return iterator{view->end(), view->end(), opaque_check_set(), filter_as_array()};
return view ? iterator{view->end(), view->end(), opaque_check_set(), filter} : iterator{};
}
/**
@ -382,9 +421,13 @@ public:
* otherwise.
*/
[[nodiscard]] entity_type back() const noexcept {
auto it = view->rbegin();
for(const auto last = view->rend(); it != last && !contains(*it); ++it) {}
return it == view->rend() ? null : *it;
if(view) {
auto it = view->rbegin();
for(const auto last = view->rend(); it != last && !contains(*it); ++it) {}
return it == view->rend() ? null : *it;
}
return null;
}
/**
@ -394,7 +437,7 @@ public:
* iterator otherwise.
*/
[[nodiscard]] iterator find(const entity_type entt) const noexcept {
return contains(entt) ? iterator{view->find(entt), view->end(), opaque_check_set(), filter_as_array()} : end();
return contains(entt) ? iterator{view->find(entt), view->end(), opaque_check_set(), filter} : end();
}
/**
@ -407,11 +450,12 @@ public:
}
/**
* @brief Checks if a view is properly initialized.
* @return True if the view is properly initialized, false otherwise.
* @brief Checks if a view is fully initialized.
* @return True if the view is fully initialized, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return view != nullptr;
return std::apply([](const auto *...curr) { return ((curr != nullptr) && ...); }, pools)
&& std::apply([](const auto *...curr) { return ((curr != nullptr) && ...); }, filter);
}
/**
@ -420,8 +464,7 @@ public:
* @return True if the view contains the given entity, false otherwise.
*/
[[nodiscard]] bool contains(const entity_type entt) const noexcept {
return std::apply([entt](const auto *...curr) { return (curr->contains(entt) && ...); }, pools)
&& std::apply([entt](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter);
return view && std::apply([entt](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) && internal::none_of(filter, entt);
}
/**
@ -431,39 +474,33 @@ public:
* Attempting to use an entity that doesn't belong to the view results in
* undefined behavior.
*
* @tparam Type Types of components to get.
* @tparam Type Type of the component to get.
* @tparam Other Other types of components to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
*/
template<typename... Type>
template<typename Type, typename... Other>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
if constexpr(sizeof...(Type) == 0) {
return get<index_of<Type>, index_of<Other>...>(entt);
}
/**
* @brief Returns the components assigned to the given entity.
*
* @sa get
*
* @tparam Index Indexes of the components to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
*/
template<std::size_t... Index>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
if constexpr(sizeof...(Index) == 0) {
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools);
} else if constexpr(sizeof...(Type) == 1) {
return (storage<index_of<Type>>().get(entt), ...);
} else if constexpr(sizeof...(Index) == 1) {
return (std::get<Index>(pools)->get(entt), ...);
} else {
return std::tuple_cat(storage<index_of<Type>>().get_as_tuple(entt)...);
}
}
/**
* @brief Returns the components assigned to the given entity.
*
* @warning
* Attempting to use an entity that doesn't belong to the view results in
* undefined behavior.
*
* @tparam First Index of a component to get.
* @tparam Other Indexes of other components to get.
* @param entt A valid identifier.
* @return The components assigned to the entity.
*/
template<std::size_t First, std::size_t... Other>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
if constexpr(sizeof...(Other) == 0) {
return storage<First>().get(entt);
} else {
return std::tuple_cat(storage<First>().get_as_tuple(entt), storage<Other>().get_as_tuple(entt)...);
return std::tuple_cat(std::get<Index>(pools)->get_as_tuple(entt)...);
}
}
@ -487,7 +524,7 @@ public:
*/
template<typename Func>
void each(Func func) const {
pick_and_each(func, std::index_sequence_for<Get...>{});
view ? pick_and_each(func, std::index_sequence_for<Get...>{}) : void();
}
/**
@ -512,14 +549,16 @@ public:
*/
template<typename... OGet, typename... OExclude>
[[nodiscard]] auto operator|(const basic_view<get_t<OGet...>, exclude_t<OExclude...>> &other) const noexcept {
return std::make_from_tuple<basic_view<get_t<Get..., OGet...>, exclude_t<Exclude..., OExclude...>>>(
std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, std::tuple_cat(pools, other.pools, filter, other.filter)));
return internal::view_pack(
std::tuple_cat(pools, other.pools),
std::tuple_cat(internal::filter_as_tuple<Exclude...>(filter), internal::filter_as_tuple<OExclude...>(other.filter)),
std::index_sequence_for<Get..., OGet..., Exclude..., OExclude...>{});
}
private:
std::tuple<Get *...> pools;
std::tuple<Exclude *...> filter;
const base_type *view;
std::array<const common_type *, sizeof...(Exclude)> filter;
const common_type *view;
};
/**
@ -538,13 +577,13 @@ private:
* or removed from it).
* * The entity currently pointed is destroyed.
*
* In all other cases, modifying the pool iterated by the view in any way
* invalidates all the iterators and using them results in undefined behavior.
* In all other cases, modifying the storage iterated by the view in any way
* invalidates all the iterators.
*
* @tparam Get Type of storage iterated by the view.
*/
template<typename Get>
class basic_view<get_t<Get>, exclude_t<>, std::void_t<std::enable_if_t<!component_traits<typename Get::value_type>::in_place_delete>>> {
class basic_view<get_t<Get>, exclude_t<>, std::void_t<std::enable_if_t<!Get::traits_type::in_place_delete>>> {
template<typename, typename, typename>
friend class basic_view;
@ -554,62 +593,81 @@ public:
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Common type among all storage types. */
using base_type = typename Get::base_type;
using common_type = typename Get::base_type;
/*! @brief Random access iterator type. */
using iterator = typename base_type::iterator;
using iterator = typename common_type::iterator;
/*! @brief Reversed iterator type. */
using reverse_iterator = typename base_type::reverse_iterator;
using reverse_iterator = typename common_type::reverse_iterator;
/*! @brief Iterable view type. */
using iterable = decltype(std::declval<Get>().each());
/*! @brief Default constructor to use to create empty, invalid views. */
basic_view() noexcept
: pools{},
filter{} {}
filter{},
view{} {}
/**
* @brief Constructs a single-type view from a storage class.
* @param ref The storage for the type to iterate.
* @param value The storage for the type to iterate.
*/
basic_view(Get &ref) noexcept
: pools{&ref},
filter{} {}
basic_view(Get &value) noexcept
: pools{&value},
filter{},
view{&value} {}
/**
* @brief Constructs a single-type view from a storage class.
* @param ref The storage for the type to iterate.
* @param value The storage for the type to iterate.
*/
basic_view(std::tuple<Get &> ref, std::tuple<> = {}) noexcept
: pools{&std::get<0>(ref)},
filter{} {}
basic_view(std::tuple<Get &> value, std::tuple<> = {}) noexcept
: basic_view{std::get<0>(value)} {}
/**
* @brief Returns the leading storage of a view.
* @brief Returns the leading storage of a view, if any.
* @return The leading storage of the view.
*/
[[nodiscard]] const base_type &handle() const noexcept {
return storage();
[[nodiscard]] const common_type *handle() const noexcept {
return view;
}
/**
* @brief Returns the storage for a given component type.
* @brief Returns the storage for a given component type, if any.
* @tparam Type Type of component of which to return the storage.
* @return The storage for the given component type.
*/
template<typename Type = typename Get::value_type>
[[nodiscard]] decltype(auto) storage() const noexcept {
[[nodiscard]] auto *storage() const noexcept {
static_assert(std::is_same_v<std::remove_const_t<Type>, typename Get::value_type>, "Invalid component type");
return storage<0>();
}
/**
* @brief Returns the storage for a given index.
* @brief Returns the storage for a given index, if any.
* @tparam Index Index of the storage to return.
* @return The storage for the given index.
*/
template<std::size_t Index>
[[nodiscard]] decltype(auto) storage() const noexcept {
return *std::get<Index>(pools);
[[nodiscard]] auto *storage() const noexcept {
return std::get<Index>(pools);
}
/**
* @brief Assigns a storage to a view.
* @param elem A storage to assign to the view.
*/
void storage(Get &elem) noexcept {
storage<0>(elem);
}
/**
* @brief Assigns a storage to a view.
* @tparam Index Index of the storage to assign to the view.
* @param elem A storage to assign to the view.
*/
template<std::size_t Index>
void storage(Get &elem) noexcept {
view = std::get<Index>(pools) = &elem;
}
/**
@ -617,7 +675,7 @@ public:
* @return Number of entities that have the given component.
*/
[[nodiscard]] size_type size() const noexcept {
return handle().size();
return view ? view->size() : size_type{};
}
/**
@ -625,59 +683,47 @@ public:
* @return True if the view is empty, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
return handle().empty();
return !view || view->empty();
}
/**
* @brief Returns an iterator to the first entity of the view.
*
* The returned iterator points to the first entity of the view. If the view
* is empty, the returned iterator will be equal to `end()`.
* If the view is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first entity of the view.
*/
[[nodiscard]] iterator begin() const noexcept {
return handle().begin();
return view ? view->begin() : iterator{};
}
/**
* @brief Returns an iterator that is past the last entity of the view.
*
* The returned iterator points to the entity following the last entity of
* the view. Attempting to dereference the returned iterator results in
* undefined behavior.
*
* @return An iterator to the entity following the last entity of the view.
*/
[[nodiscard]] iterator end() const noexcept {
return handle().end();
return view ? view->end() : iterator{};
}
/**
* @brief Returns an iterator to the first entity of the reversed view.
*
* The returned iterator points to the first entity of the reversed view. If
* the view is empty, the returned iterator will be equal to `rend()`.
* If the view is empty, the returned iterator will be equal to `rend()`.
*
* @return An iterator to the first entity of the reversed view.
*/
[[nodiscard]] reverse_iterator rbegin() const noexcept {
return handle().rbegin();
return view ? view->rbegin() : reverse_iterator{};
}
/**
* @brief Returns an iterator that is past the last entity of the reversed
* view.
*
* The returned iterator points to the entity following the last entity of
* the reversed view. Attempting to dereference the returned iterator
* results in undefined behavior.
*
* @return An iterator to the entity following the last entity of the
* reversed view.
*/
[[nodiscard]] reverse_iterator rend() const noexcept {
return handle().rend();
return view ? view->rend() : reverse_iterator{};
}
/**
@ -686,7 +732,7 @@ public:
* otherwise.
*/
[[nodiscard]] entity_type front() const noexcept {
return empty() ? null : *begin();
return (!view || view->empty()) ? null : *view->begin();
}
/**
@ -695,7 +741,7 @@ public:
* otherwise.
*/
[[nodiscard]] entity_type back() const noexcept {
return empty() ? null : *rbegin();
return (!view || view->empty()) ? null : *view->rbegin();
}
/**
@ -705,7 +751,7 @@ public:
* iterator otherwise.
*/
[[nodiscard]] iterator find(const entity_type entt) const noexcept {
return contains(entt) ? handle().find(entt) : end();
return view ? view->find(entt) : iterator{};
}
/**
@ -723,15 +769,15 @@ public:
* @return The component assigned to the given entity.
*/
[[nodiscard]] decltype(auto) operator[](const entity_type entt) const {
return storage().get(entt);
return std::get<0>(pools)->get(entt);
}
/**
* @brief Checks if a view is properly initialized.
* @return True if the view is properly initialized, false otherwise.
* @brief Checks if a view is fully initialized.
* @return True if the view is fully initialized, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return std::get<0>(pools) != nullptr;
return (std::get<0>(pools) != nullptr);
}
/**
@ -740,7 +786,7 @@ public:
* @return True if the view contains the given entity, false otherwise.
*/
[[nodiscard]] bool contains(const entity_type entt) const noexcept {
return handle().contains(entt);
return view && view->contains(entt);
}
/**
@ -750,24 +796,24 @@ public:
* Attempting to use an entity that doesn't belong to the view results in
* undefined behavior.
*
* @tparam Type Type or index of the component to get.
* @tparam Elem Type or index of the component to get.
* @param entt A valid identifier.
* @return The component assigned to the entity.
*/
template<typename... Type>
template<typename Elem>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
if constexpr(sizeof...(Type) == 0) {
return storage().get_as_tuple(entt);
} else {
static_assert((std::is_same_v<std::remove_const_t<Type>, typename Get::value_type> && ...), "Invalid component type");
return storage().get(entt);
}
static_assert(std::is_same_v<std::remove_const_t<Elem>, typename Get::value_type>, "Invalid component type");
return get<0>(entt);
}
/*! @copydoc get */
template<std::size_t Index>
template<std::size_t... Elem>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
return storage().get(entt);
if constexpr(sizeof...(Elem) == 0) {
return std::get<0>(pools)->get_as_tuple(entt);
} else {
return std::get<Elem...>(pools)->get(entt);
}
}
/**
@ -794,17 +840,19 @@ public:
*/
template<typename Func>
void each(Func func) const {
if constexpr(is_applicable_v<Func, decltype(*each().begin())>) {
for(const auto pack: each()) {
std::apply(func, pack);
}
} else if constexpr(ignore_as_empty_v<typename Get::value_type>) {
for(size_type pos{}, last = size(); pos < last; ++pos) {
func();
}
} else {
for(auto &&component: storage()) {
func(component);
if(view) {
if constexpr(is_applicable_v<Func, decltype(*each().begin())>) {
for(const auto pack: each()) {
std::apply(func, pack);
}
} else if constexpr(Get::traits_type::page_size == 0u) {
for(size_type pos{}, last = size(); pos < last; ++pos) {
func();
}
} else {
for(auto &&component: *std::get<0>(pools)) {
func(component);
}
}
}
}
@ -819,7 +867,7 @@ public:
* @return An iterable object to use to _visit_ the view.
*/
[[nodiscard]] iterable each() const noexcept {
return storage().each();
return view ? std::get<0>(pools)->each() : iterable{};
}
/**
@ -831,13 +879,16 @@ public:
*/
template<typename... OGet, typename... OExclude>
[[nodiscard]] auto operator|(const basic_view<get_t<OGet...>, exclude_t<OExclude...>> &other) const noexcept {
return std::make_from_tuple<basic_view<get_t<Get, OGet...>, exclude_t<OExclude...>>>(
std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, std::tuple_cat(pools, other.pools, other.filter)));
return internal::view_pack(
std::tuple_cat(pools, other.pools),
internal::filter_as_tuple<OExclude...>(other.filter),
std::index_sequence_for<Get, OGet..., OExclude...>{});
}
private:
std::tuple<Get *> pools;
std::tuple<> filter;
std::array<const common_type *, 0u> filter;
const common_type *view;
};
/**

View File

@ -24,6 +24,7 @@
#include "entity/group.hpp"
#include "entity/handle.hpp"
#include "entity/helper.hpp"
#include "entity/mixin.hpp"
#include "entity/observer.hpp"
#include "entity/organizer.hpp"
#include "entity/registry.hpp"
@ -31,7 +32,6 @@
#include "entity/snapshot.hpp"
#include "entity/sparse_set.hpp"
#include "entity/storage.hpp"
#include "entity/storage_mixin.hpp"
#include "entity/view.hpp"
#include "graph/adjacency_matrix.hpp"
#include "graph/dot.hpp"

View File

@ -258,7 +258,7 @@ public:
[[nodiscard]] iterable_adaptor<out_edge_iterator> out_edges(const vertex_type vertex) const noexcept {
const auto it = matrix.cbegin();
const auto from = vertex * vert;
const auto to = vertex * vert + vert;
const auto to = from + vert;
return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}};
}
@ -270,7 +270,7 @@ public:
[[nodiscard]] iterable_adaptor<in_edge_iterator> in_edges(const vertex_type vertex) const noexcept {
const auto it = matrix.cbegin();
const auto from = vertex;
const auto to = vert * (vert - 1u) + vertex;
const auto to = vert * vert + from;
return {{it, vert, from, to, vert}, {it, vert, to, to, vert}};
}

View File

@ -32,6 +32,7 @@ class basic_flow {
using task_container_type = dense_set<id_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<id_type>>;
using ro_rw_container_type = std::vector<std::pair<std::size_t, bool>, typename alloc_traits::template rebind_alloc<std::pair<std::size_t, bool>>>;
using deps_container_type = dense_map<id_type, ro_rw_container_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, ro_rw_container_type>>>;
using adjacency_matrix_type = adjacency_matrix<directed_tag, typename alloc_traits::template rebind_alloc<std::size_t>>;
void emplace(const id_type res, const bool is_rw) {
ENTT_ASSERT(index.first() < vertices.size(), "Invalid node");
@ -43,6 +44,76 @@ class basic_flow {
deps[res].emplace_back(index.first(), is_rw);
}
void setup_graph(adjacency_matrix_type &matrix) const {
for(const auto &elem: deps) {
const auto last = elem.second.cend();
auto it = elem.second.cbegin();
while(it != last) {
if(it->second) {
// rw item
if(auto curr = it++; it != last) {
if(it->second) {
matrix.insert(curr->first, it->first);
} else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) {
for(; it != next; ++it) {
matrix.insert(curr->first, it->first);
matrix.insert(it->first, next->first);
}
} else {
for(; it != next; ++it) {
matrix.insert(curr->first, it->first);
}
}
}
} else {
// ro item (first iteration only)
if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) {
for(; it != next; ++it) {
matrix.insert(it->first, next->first);
}
} else {
it = last;
}
}
}
}
}
void transitive_closure(adjacency_matrix_type &matrix) const {
const auto length = matrix.size();
for(std::size_t vk{}; vk < length; ++vk) {
for(std::size_t vi{}; vi < length; ++vi) {
for(std::size_t vj{}; vj < length; ++vj) {
if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) {
matrix.insert(vi, vj);
}
}
}
}
}
void transitive_reduction(adjacency_matrix_type &matrix) const {
const auto length = matrix.size();
for(std::size_t vert{}; vert < length; ++vert) {
matrix.erase(vert, vert);
}
for(std::size_t vj{}; vj < length; ++vj) {
for(std::size_t vi{}; vi < length; ++vi) {
if(matrix.contains(vi, vj)) {
for(std::size_t vk{}; vk < length; ++vk) {
if(matrix.contains(vj, vk)) {
matrix.erase(vi, vk);
}
}
}
}
}
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
@ -50,6 +121,8 @@ public:
using size_type = std::size_t;
/*! @brief Iterable task list. */
using iterable = iterable_adaptor<typename task_container_type::const_iterator>;
/*! @brief Adjacency matrix type. */
using graph_type = adjacency_matrix_type;
/*! @brief Default constructor. */
basic_flow()
@ -124,9 +197,10 @@ public:
/*! @brief Clears the flow builder. */
void clear() noexcept {
index.first() = sync_on = {};
index.first() = {};
vertices.clear();
deps.clear();
sync_on = {};
}
/**
@ -245,72 +319,12 @@ public:
* @brief Generates a task graph for the current content.
* @return The adjacency matrix of the task graph.
*/
[[nodiscard]] adjacency_matrix<directed_tag> graph() const {
const auto length = vertices.size();
adjacency_matrix<directed_tag> matrix{length};
[[nodiscard]] graph_type graph() const {
graph_type matrix{vertices.size(), get_allocator()};
// creates the adjacency matrix
for(const auto &elem: deps) {
const auto last = elem.second.cend();
auto it = elem.second.cbegin();
while(it != last) {
if(it->second) {
// rw item
if(auto curr = it++; it != last) {
if(it->second) {
matrix.insert(curr->first, it->first);
} else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) {
for(; it != next; ++it) {
matrix.insert(curr->first, it->first);
matrix.insert(it->first, next->first);
}
} else {
for(; it != next; ++it) {
matrix.insert(curr->first, it->first);
}
}
}
} else {
// ro item (first iteration only)
if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) {
for(; it != next; ++it) {
matrix.insert(it->first, next->first);
}
} else {
it = last;
}
}
}
}
// computes the transitive closure
for(std::size_t vk{}; vk < length; ++vk) {
for(std::size_t vi{}; vi < length; ++vi) {
for(std::size_t vj{}; vj < length; ++vj) {
if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) {
matrix.insert(vi, vj);
}
}
}
}
// applies the transitive reduction
for(std::size_t vert{}; vert < length; ++vert) {
matrix.erase(vert, vert);
}
for(std::size_t vj{}; vj < length; ++vj) {
for(std::size_t vi{}; vi < length; ++vi) {
if(matrix.contains(vi, vj)) {
for(std::size_t vk{}; vk < length; ++vk) {
if(matrix.contains(vj, vk)) {
matrix.erase(vi, vk);
}
}
}
}
}
setup_graph(matrix);
transitive_closure(matrix);
transitive_reduction(matrix);
return matrix;
}

View File

@ -70,40 +70,40 @@ public:
* cases, they are discarded.
*
* @tparam Args Types of arguments to use to construct the fallback service.
* @tparam Impl Fallback service type.
* @tparam Type Fallback service type.
* @param args Parameters to use to construct the fallback service.
* @return A reference to a valid service.
*/
template<typename Impl = Service, typename... Args>
template<typename Type = Service, typename... Args>
[[nodiscard]] static Service &value_or(Args &&...args) {
return service ? *service : emplace<Impl>(std::forward<Args>(args)...);
return service ? *service : emplace<Type>(std::forward<Args>(args)...);
}
/**
* @brief Sets or replaces a service.
* @tparam Impl Service type.
* @tparam Type Service type.
* @tparam Args Types of arguments to use to construct the service.
* @param args Parameters to use to construct the service.
* @return A reference to a valid service.
*/
template<typename Impl = Service, typename... Args>
template<typename Type = Service, typename... Args>
static Service &emplace(Args &&...args) {
service = std::make_shared<Impl>(std::forward<Args>(args)...);
service = std::make_shared<Type>(std::forward<Args>(args)...);
return *service;
}
/**
* @brief Sets or replaces a service using a given allocator.
* @tparam Impl Service type.
* @tparam Type Service type.
* @tparam Allocator Type of allocator used to manage memory and elements.
* @tparam Args Types of arguments to use to construct the service.
* @param alloc The allocator to use.
* @param args Parameters to use to construct the service.
* @return A reference to a valid service.
*/
template<typename Impl = Service, typename Allocator, typename... Args>
static Service &allocate_emplace(Allocator alloc, Args &&...args) {
service = std::allocate_shared<Impl>(alloc, std::forward<Args>(args)...);
template<typename Type = Service, typename Allocator, typename... Args>
static Service &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) {
service = std::allocate_shared<Type>(alloc, std::forward<Args>(args)...);
return *service;
}
@ -125,6 +125,18 @@ public:
service = other.value;
}
/**
* @brief Resets or replaces a service.
* @tparam Type Service type.
* @tparam Deleter Deleter type.
* @param elem A pointer to a service to manage.
* @param deleter A deleter to use to destroy the service.
*/
template<typename Type, typename Deleter = std::default_delete<Type>>
static void reset(Type *elem, Deleter deleter = {}) {
service = std::shared_ptr<Service>{elem, std::move(deleter)};
}
private:
// std::shared_ptr because of its type erased allocator which is useful here
inline static std::shared_ptr<Service> service{};

View File

@ -21,8 +21,8 @@ struct meta_type_node;
struct meta_context {
dense_map<id_type, meta_type_node, identity> value{};
static inline meta_context &from(meta_ctx &ctx);
static inline const meta_context &from(const meta_ctx &ctx);
[[nodiscard]] static inline meta_context &from(meta_ctx &ctx);
[[nodiscard]] static inline const meta_context &from(const meta_ctx &ctx);
};
} // namespace internal
@ -49,11 +49,11 @@ class meta_ctx: private internal::meta_context {
* Internal details not to be documented.
*/
inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) {
[[nodiscard]] inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) {
return ctx;
}
inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) {
[[nodiscard]] inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) {
return ctx;
}

View File

@ -29,28 +29,12 @@ namespace entt {
namespace internal {
inline decltype(auto) owner(meta_ctx &ctx, const type_info &info) {
[[nodiscard]] inline decltype(auto) owner(meta_ctx &ctx, const type_info &info) {
auto &&context = internal::meta_context::from(ctx);
ENTT_ASSERT(context.value.contains(info.hash()), "Type not available");
return context.value[info.hash()];
}
inline meta_base_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_base_node node) {
return parent.details->base.insert_or_assign(id, std::move(node)).first->second;
}
inline meta_conv_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_conv_node node) {
return parent.details->conv.insert_or_assign(id, std::move(node)).first->second;
}
inline meta_ctor_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_ctor_node node) {
return parent.details->ctor.insert_or_assign(id, std::move(node)).first->second;
}
inline meta_dtor_node &meta_extend(internal::meta_type_node &parent, meta_dtor_node node) {
return (parent.dtor = std::move(node));
}
inline meta_data_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_data_node node) {
return parent.details->data.insert_or_assign(id, std::move(node)).first->second;
}
@ -72,10 +56,6 @@ inline meta_func_node &meta_extend(internal::meta_type_node &parent, const id_ty
return parent.details->func.insert_or_assign(id, std::move(node)).first->second;
}
inline meta_prop_node &meta_extend(dense_map<id_type, meta_prop_node, identity> &prop, const id_type id, meta_prop_node node) {
return (prop[id] = std::move(node));
}
} // namespace internal
/**
@ -156,16 +136,8 @@ public:
template<typename Base>
auto base() noexcept {
static_assert(!std::is_same_v<Type, Base> && std::is_base_of_v<Base, Type>, "Invalid base type");
internal::meta_extend(
internal::owner(*ctx, *info),
type_id<Base>().hash(),
internal::meta_base_node{
&internal::resolve<Base>,
+[](const void *instance) noexcept {
return static_cast<const void *>(static_cast<const Base *>(static_cast<const Type *>(instance)));
}});
auto *const op = +[](const void *instance) noexcept { return static_cast<const void *>(static_cast<const Base *>(static_cast<const Type *>(instance))); };
internal::owner(*ctx, *info).details->base.insert_or_assign(type_id<Base>().hash(), internal::meta_base_node{&internal::resolve<Base>, op});
bucket = nullptr;
return *this;
}
@ -185,15 +157,8 @@ 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 &>>>;
internal::meta_extend(
internal::owner(*ctx, *info),
type_id<conv_type>().hash(),
internal::meta_conv_node{
+[](const meta_ctx &area, const void *instance) {
return forward_as_meta(area, std::invoke(Candidate, *static_cast<const Type *>(instance)));
}});
auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, std::invoke(Candidate, *static_cast<const Type *>(instance))); };
internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id<conv_type>().hash(), internal::meta_conv_node{op});
bucket = nullptr;
return *this;
}
@ -210,15 +175,8 @@ public:
template<typename To>
auto conv() noexcept {
using conv_type = std::remove_cv_t<std::remove_reference_t<To>>;
internal::meta_extend(
internal::owner(*ctx, *info),
type_id<conv_type>().hash(),
internal::meta_conv_node{
+[](const meta_ctx &area, const void *instance) {
return forward_as_meta(area, static_cast<To>(*static_cast<const Type *>(instance)));
}});
auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, static_cast<To>(*static_cast<const Type *>(instance))); };
internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id<conv_type>().hash(), internal::meta_conv_node{op});
bucket = nullptr;
return *this;
}
@ -241,15 +199,7 @@ public:
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");
internal::meta_extend(
internal::owner(*ctx, *info),
type_id<typename descriptor::args_type>().hash(),
internal::meta_ctor_node{
descriptor::args_type::size,
&meta_arg<typename descriptor::args_type>,
&meta_construct<Type, Candidate, Policy>});
internal::owner(*ctx, *info).details->ctor.insert_or_assign(type_id<typename descriptor::args_type>().hash(), internal::meta_ctor_node{descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Candidate, Policy>});
bucket = nullptr;
return *this;
}
@ -269,14 +219,7 @@ public:
// default constructor is already implicitly generated, no need for redundancy
if constexpr(sizeof...(Args) != 0u) {
using descriptor = meta_function_helper_t<Type, Type (*)(Args...)>;
internal::meta_extend(
internal::owner(*ctx, *info),
type_id<typename descriptor::args_type>().hash(),
internal::meta_ctor_node{
descriptor::args_type::size,
&meta_arg<typename descriptor::args_type>,
&meta_construct<Type, Args...>});
internal::owner(*ctx, *info).details->ctor.insert_or_assign(type_id<typename descriptor::args_type>().hash(), internal::meta_ctor_node{descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Args...>});
}
bucket = nullptr;
@ -304,12 +247,8 @@ public:
template<auto Func>
auto dtor() noexcept {
static_assert(std::is_invocable_v<decltype(Func), Type &>, "The function doesn't accept an object of the type provided");
internal::meta_extend(
internal::owner(*ctx, *info),
internal::meta_dtor_node{
+[](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); }});
auto *const op = +[](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); };
internal::owner(*ctx, *info).dtor = internal::meta_dtor_node{op};
bucket = nullptr;
return *this;
}
@ -330,32 +269,39 @@ public:
template<auto Data, typename Policy = as_is_t>
auto data(const id_type id) noexcept {
if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
using data_type = std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>;
using data_type = std::invoke_result_t<decltype(Data), Type &>;
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
internal::meta_data_node{
/* this is never static */
std::is_const_v<data_type> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
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<data_type>>,
&meta_arg<type_list<std::remove_cv_t<data_type>>>,
&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>>>>,
&meta_setter<Type, Data>,
&meta_getter<Type, Data, Policy>});
bucket = &elem.prop;
} else {
using data_type = std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>;
using data_type = std::remove_pointer_t<decltype(Data)>;
if constexpr(std::is_pointer_v<decltype(Data)>) {
static_assert(Policy::template value<decltype(*Data)>, "Invalid return type for the given policy");
} else {
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
}
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
internal::meta_data_node{
((std::is_same_v<Type, std::remove_cv_t<data_type>> || std::is_const_v<data_type>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static,
((std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<data_type>>> || std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static,
1u,
&internal::resolve<std::remove_cv_t<data_type>>,
&meta_arg<type_list<std::remove_cv_t<data_type>>>,
&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>>>>,
&meta_setter<Type, Data>,
&meta_getter<Type, Data, Policy>});
@ -495,18 +441,11 @@ public:
ENTT_ASSERT(bucket != nullptr, "Meta object does not support properties");
if constexpr(sizeof...(Value) == 0u) {
internal::meta_extend(
*bucket,
id,
internal::meta_prop_node{
&internal::resolve<void>});
(*bucket)[id] = internal::meta_prop_node{&internal::resolve<void>};
} else {
internal::meta_extend(
*bucket,
id,
internal::meta_prop_node{
&internal::resolve<std::decay_t<Value>>...,
std::make_shared<std::decay_t<Value>>(std::forward<Value>(value))...});
(*bucket)[id] = internal::meta_prop_node{
&internal::resolve<std::decay_t<Value>>...,
std::make_shared<std::decay_t<Value>>(std::forward<Value>(value))...};
}
return *this;

View File

@ -613,7 +613,7 @@ private:
* @return A properly initialized and not necessarily owning wrapper.
*/
template<typename Type>
meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) {
[[nodiscard]] meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) {
return meta_any{ctx, std::in_place_type<Type &&>, std::forward<Type>(value)};
}
@ -624,7 +624,7 @@ meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) {
* @return A properly initialized and not necessarily owning wrapper.
*/
template<typename Type>
meta_any forward_as_meta(Type &&value) {
[[nodiscard]] meta_any forward_as_meta(Type &&value) {
return forward_as_meta(locator<meta_ctx>::value_or(), std::forward<Type>(value));
}
@ -722,6 +722,16 @@ struct meta_handle {
return static_cast<bool>(any);
}
/*! @copydoc meta_any::operator== */
[[nodiscard]] bool operator==(const meta_handle &other) const noexcept {
return (any == other.any);
}
/*! @copydoc meta_any::operator!= */
[[nodiscard]] bool operator!=(const meta_handle &other) const noexcept {
return !(*this == other);
}
/**
* @brief Access operator for accessing the contained opaque object.
* @return A wrapper that shares a reference to an unmanaged object.
@ -756,13 +766,21 @@ struct meta_prop {
ctx{&area} {}
/**
* @brief Returns the stored value by copy.
* @brief Returns the stored value by const reference.
* @return A wrapper containing the value stored with the property.
*/
[[nodiscard]] meta_any value() const {
return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, node->value.get()) : meta_any{meta_ctx_arg, *ctx};
}
/**
* @brief Returns the stored value by reference.
* @return A wrapper containing the value stored with the property.
*/
[[nodiscard]] meta_any value() {
return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, node->value.get(), nullptr) : meta_any{meta_ctx_arg, *ctx};
}
/**
* @brief Returns true if an object is valid, false otherwise.
* @return True if the object is valid, false otherwise.
@ -771,11 +789,30 @@ struct meta_prop {
return (node != nullptr);
}
/**
* @brief Checks if two objects refer to the same type.
* @param other The object with which to compare.
* @return True if the objects refer to the same type, false otherwise.
*/
[[nodiscard]] bool operator==(const meta_prop &other) const noexcept {
return (ctx == other.ctx && node == other.node);
}
private:
const internal::meta_prop_node *node;
const meta_ctx *ctx;
};
/**
* @brief Checks if two objects refer to the same type.
* @param lhs An object, either valid or not.
* @param rhs An object, either valid or not.
* @return False if the objects refer to the same node, true otherwise.
*/
[[nodiscard]] inline bool operator!=(const meta_prop &lhs, const meta_prop &rhs) noexcept {
return !(lhs == rhs);
}
/*! @brief Opaque wrapper for data members. */
struct meta_data {
/*! @brief Unsigned integer type. */
@ -876,11 +913,26 @@ struct meta_data {
return (node != nullptr);
}
/*! @copydoc meta_prop::operator== */
[[nodiscard]] bool operator==(const meta_data &other) const noexcept {
return (ctx == other.ctx && node == other.node);
}
private:
const internal::meta_data_node *node;
const meta_ctx *ctx;
};
/**
* @brief Checks if two objects refer to the same type.
* @param lhs An object, either valid or not.
* @param rhs An object, either valid or not.
* @return False if the objects refer to the same node, true otherwise.
*/
[[nodiscard]] inline bool operator!=(const meta_data &lhs, const meta_data &rhs) noexcept {
return !(lhs == rhs);
}
/*! @brief Opaque wrapper for member functions. */
struct meta_func {
/*! @brief Unsigned integer type. */
@ -962,8 +1014,12 @@ struct meta_func {
*/
template<typename... Args>
meta_any invoke(meta_handle instance, Args &&...args) const {
meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward<Args>(args)}...};
return invoke(meta_handle{*ctx, std::move(instance)}, arguments, sizeof...(Args));
if constexpr(sizeof...(Args) == 0u) {
return invoke(std::move(instance), static_cast<meta_any *>(nullptr), size_type{});
} else {
meta_any arguments[sizeof...(Args)]{{*ctx, std::forward<Args>(args)}...};
return invoke(std::move(instance), arguments, sizeof...(Args));
}
}
/*! @copydoc meta_data::prop */
@ -997,11 +1053,26 @@ struct meta_func {
return (node != nullptr);
}
/*! @copydoc meta_prop::operator== */
[[nodiscard]] bool operator==(const meta_func &other) const noexcept {
return (ctx == other.ctx && node == other.node);
}
private:
const internal::meta_func_node *node;
const meta_ctx *ctx;
};
/**
* @brief Checks if two objects refer to the same type.
* @param lhs An object, either valid or not.
* @param rhs An object, either valid or not.
* @return False if the objects refer to the same node, true otherwise.
*/
[[nodiscard]] inline bool operator!=(const meta_func &lhs, const meta_func &rhs) noexcept {
return !(lhs == rhs);
}
/*! @brief Opaque wrapper for types. */
class meta_type {
template<typename Func>
@ -1339,8 +1410,12 @@ public:
*/
template<typename... Args>
[[nodiscard]] meta_any construct(Args &&...args) const {
meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward<Args>(args)}...};
return construct(arguments, sizeof...(Args));
if constexpr(sizeof...(Args) == 0u) {
return construct(static_cast<meta_any *>(nullptr), size_type{});
} else {
meta_any arguments[sizeof...(Args)]{{*ctx, std::forward<Args>(args)}...};
return construct(arguments, sizeof...(Args));
}
}
/**
@ -1348,12 +1423,12 @@ public:
* @param element A valid pointer to an element of the underlying type.
* @return A wrapper that references the given instance.
*/
meta_any from_void(void *element) const {
[[nodiscard]] meta_any from_void(void *element) const {
return (element && node.from_void) ? node.from_void(*ctx, element, nullptr) : meta_any{meta_ctx_arg, *ctx};
}
/*! @copydoc from_void */
meta_any from_void(const void *element) const {
[[nodiscard]] meta_any from_void(const void *element) const {
return (element && node.from_void) ? node.from_void(*ctx, nullptr, element) : meta_any{meta_ctx_arg, *ctx};
}
@ -1373,7 +1448,7 @@ public:
meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const {
if(node.details) {
if(auto it = node.details->func.find(id); it != node.details->func.cend()) {
if(const auto *candidate = lookup(args, sz, (instance->data() == nullptr), [curr = &it->second]() mutable { return curr ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) {
if(const auto *candidate = lookup(args, sz, instance && (instance->data() == nullptr), [curr = &it->second]() mutable { return curr ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) {
return candidate->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args);
}
}
@ -1399,8 +1474,12 @@ public:
*/
template<typename... Args>
meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const {
meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward<Args>(args)}...};
return invoke(id, meta_handle{*ctx, std::move(instance)}, arguments, sizeof...(Args));
if constexpr(sizeof...(Args) == 0u) {
return invoke(id, std::move(instance), static_cast<meta_any *>(nullptr), size_type{});
} else {
meta_any arguments[sizeof...(Args)]{{*ctx, std::forward<Args>(args)}...};
return invoke(id, std::move(instance), arguments, sizeof...(Args));
}
}
/**
@ -1466,11 +1545,7 @@ public:
return !(ctx == nullptr);
}
/**
* @brief Checks if two objects refer to the same type.
* @param other The object with which to compare.
* @return True if the objects refer to the same type, false otherwise.
*/
/*! @copydoc meta_prop::operator== */
[[nodiscard]] bool operator==(const meta_type &other) const noexcept {
return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info));
}
@ -1622,7 +1697,7 @@ public:
explicit meta_iterator(const meta_ctx &area, It iter) noexcept
: ctx{&area},
vtable{&basic_vtable<It>},
handle{std::move(iter)} {}
handle{iter} {}
meta_iterator &operator++() noexcept {
vtable(operation::incr, handle, 1, nullptr);
@ -1716,7 +1791,7 @@ public:
meta_iterator(const meta_ctx &area, std::integral_constant<bool, KeyOnly>, It iter) noexcept
: ctx{&area},
vtable{&basic_vtable<KeyOnly, It>},
handle{std::move(iter)} {}
handle{iter} {}
meta_iterator &operator++() noexcept {
vtable(operation::incr, handle, nullptr);
@ -1826,7 +1901,7 @@ inline meta_sequence_container::iterator meta_sequence_container::insert(iterato
* @return A possibly invalid iterator following the last removed element.
*/
inline meta_sequence_container::iterator meta_sequence_container::erase(iterator it) {
return insert(std::move(it), {});
return insert(it, {});
}
/**

View File

@ -68,11 +68,7 @@ struct as_void_t final {
*/
template<typename Type>
struct is_meta_policy
: std::disjunction<
std::is_same<Type, as_ref_t>,
std::is_same<Type, as_cref_t>,
std::is_same<Type, as_is_t>,
std::is_same<Type, as_void_t>> {};
: 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>> {};
/**
* @brief Helper variable template.

View File

@ -25,19 +25,19 @@ struct meta_range_iterator final {
using reference = value_type;
using iterator_category = std::input_iterator_tag;
meta_range_iterator() noexcept
constexpr meta_range_iterator() noexcept
: it{},
ctx{} {}
meta_range_iterator(const meta_ctx &area, const It iter) noexcept
constexpr meta_range_iterator(const meta_ctx &area, const It iter) noexcept
: it{iter},
ctx{&area} {}
meta_range_iterator &operator++() noexcept {
constexpr meta_range_iterator &operator++() noexcept {
return ++it, *this;
}
meta_range_iterator operator++(int) noexcept {
constexpr meta_range_iterator operator++(int) noexcept {
meta_range_iterator orig = *this;
return ++(*this), orig;
}

View File

@ -30,7 +30,6 @@ struct meta_associative_container_traits;
/**
* @brief Provides the member constant `value` to true if a given type is a
* pointer-like type from the point of view of the meta system, false otherwise.
* @tparam Type Potentially pointer-like type.
*/
template<typename>
struct is_meta_pointer_like: std::false_type {};

View File

@ -92,9 +92,12 @@ template<typename Type, typename Ret, typename MaybeType, typename... Args>
struct meta_function_descriptor<Type, Ret (*)(MaybeType, Args...)>
: meta_function_descriptor_traits<
Ret,
std::conditional_t<std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>, type_list<Args...>, type_list<MaybeType, Args...>>,
!std::is_base_of_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::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>,
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>)> {};
/**
* @brief Meta function descriptor.
@ -162,7 +165,7 @@ using meta_function_helper_t = typename meta_function_helper<Type, Candidate>::t
* @return A meta any containing the returned value, if any.
*/
template<typename Policy = as_is_t, typename Type>
std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) {
[[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>) {
@ -183,7 +186,7 @@ std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(const meta_ct
* @return A meta any containing the returned value, if any.
*/
template<typename Policy = as_is_t, typename Type>
std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(Type &&value) {
[[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));
}

View File

@ -19,7 +19,7 @@ struct poly_inspector {
* @brief Generic conversion operator (definition only).
* @tparam Type Type to which conversion is requested.
*/
template<class Type>
template<typename Type>
operator Type &&() const;
/**

View File

@ -1,13 +1,18 @@
#ifndef ENTT_PROCESS_FWD_HPP
#define ENTT_PROCESS_FWD_HPP
#include <cstdint>
namespace entt {
template<typename, typename>
class process;
template<typename>
class scheduler;
template<typename = std::uint32_t>
class basic_scheduler;
/*! @brief Alias declaration for the most common use case. */
using scheduler = basic_scheduler<>;
} // namespace entt

View File

@ -7,6 +7,7 @@
#include <type_traits>
#include <utility>
#include <vector>
#include "fwd.hpp"
#include "process.hpp"
namespace entt {
@ -38,11 +39,11 @@ namespace entt {
* @tparam Delta Type to use to provide elapsed time.
*/
template<typename Delta>
class scheduler {
class basic_scheduler {
struct process_handler {
using instance_type = std::unique_ptr<void, void (*)(void *)>;
using update_fn_type = bool(scheduler &, std::size_t, Delta, void *);
using abort_fn_type = void(scheduler &, std::size_t, bool);
using update_fn_type = bool(basic_scheduler &, std::size_t, Delta, void *);
using abort_fn_type = void(basic_scheduler &, std::size_t, bool);
using next_type = std::unique_ptr<process_handler>;
instance_type instance;
@ -58,8 +59,8 @@ class scheduler {
template<typename Proc, typename... Args>
continuation then(Args &&...args) {
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &scheduler::deleter<Proc>};
handler->next.reset(new process_handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr});
auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &basic_scheduler::deleter<Proc>};
handler->next.reset(new process_handler{std::move(proc), &basic_scheduler::update<Proc>, &basic_scheduler::abort<Proc>, nullptr});
handler = handler->next.get();
return *this;
}
@ -74,7 +75,7 @@ class scheduler {
};
template<typename Proc>
[[nodiscard]] static bool update(scheduler &owner, std::size_t pos, const Delta delta, void *data) {
[[nodiscard]] static bool update(basic_scheduler &owner, std::size_t pos, const Delta delta, void *data) {
auto *process = static_cast<Proc *>(owner.handlers[pos].instance.get());
process->tick(delta, data);
@ -94,7 +95,7 @@ class scheduler {
}
template<typename Proc>
static void abort(scheduler &owner, std::size_t pos, const bool immediately) {
static void abort(basic_scheduler &owner, std::size_t pos, const bool immediately) {
static_cast<Proc *>(owner.handlers[pos].instance.get())->abort(immediately);
}
@ -104,18 +105,20 @@ class scheduler {
}
public:
/*! @brief Unsigned integer type. */
using delta_type = Delta;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Default constructor. */
scheduler()
basic_scheduler()
: handlers{} {}
/*! @brief Default move constructor. */
scheduler(scheduler &&) = default;
basic_scheduler(basic_scheduler &&) = default;
/*! @brief Default move assignment operator. @return This scheduler. */
scheduler &operator=(scheduler &&) = default;
basic_scheduler &operator=(basic_scheduler &&) = default;
/**
* @brief Number of processes currently scheduled.
@ -171,8 +174,8 @@ public:
template<typename Proc, typename... Args>
auto attach(Args &&...args) {
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &scheduler::deleter<Proc>};
auto &&ref = handlers.emplace_back(process_handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr});
auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &basic_scheduler::deleter<Proc>};
auto &&ref = handlers.emplace_back(process_handler{std::move(proc), &basic_scheduler::update<Proc>, &basic_scheduler::abort<Proc>, nullptr});
// forces the process to exit the uninitialized state
ref.update(*this, handlers.size() - 1u, {}, nullptr);
return continuation{&handlers.back()};
@ -246,7 +249,7 @@ public:
* @param delta Elapsed time.
* @param data Optional data.
*/
void update(const Delta delta, void *data = nullptr) {
void update(const delta_type delta, void *data = nullptr) {
for(auto pos = handlers.size(); pos; --pos) {
const auto curr = pos - 1u;

View File

@ -95,51 +95,51 @@ public:
return operator*();
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
friend constexpr std::ptrdiff_t operator-(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) noexcept;
template<typename... Lhs, typename... Rhs>
friend constexpr std::ptrdiff_t operator-(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept;
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
friend constexpr bool operator==(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) noexcept;
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator==(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept;
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
friend constexpr bool operator<(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) noexcept;
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator<(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept;
private:
It it;
};
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return lhs.it - rhs.it;
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] constexpr bool operator==(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator==(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return lhs.it == rhs.it;
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] constexpr bool operator!=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator!=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] constexpr bool operator<(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator<(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return lhs.it < rhs.it;
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] constexpr bool operator>(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator>(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return rhs < lhs;
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] constexpr bool operator<=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator<=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
[[nodiscard]] constexpr bool operator>=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept {
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator>=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept {
return !(lhs < rhs);
}
@ -158,7 +158,7 @@ template<typename TLhs, typename ILhs, typename TRhs, typename IRhs>
*/
template<typename Type, typename Loader, typename Allocator>
class resource_cache {
using alloc_traits = typename std::allocator_traits<Allocator>;
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const id_type, typename Loader::result_type>>;
using container_type = dense_map<id_type, typename Loader::result_type, identity, std::equal_to<id_type>, container_allocator>;
@ -241,8 +241,7 @@ public:
/**
* @brief Returns an iterator to the beginning.
*
* The returned iterator points to the first instance of the cache. If the
* cache is empty, the returned iterator will be equal to `end()`.
* If the cache is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first instance of the internal cache.
*/
@ -262,11 +261,6 @@ public:
/**
* @brief Returns an iterator to the end.
*
* The returned iterator points to the element following the last instance
* of the cache. Attempting to dereference the returned iterator results in
* undefined behavior.
*
* @return An iterator to the element following the last instance of the
* internal cache.
*/

View File

@ -163,81 +163,81 @@ private:
/**
* @brief Compares two handles.
* @tparam Res Type of resource managed by the first handle.
* @tparam Other Type of resource managed by the second handle.
* @tparam Lhs Type of resource managed by the first handle.
* @tparam Rhs Type of resource managed by the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return True if both handles refer to the same resource, false otherwise.
*/
template<typename Res, typename Other>
[[nodiscard]] bool operator==(const resource<Res> &lhs, const resource<Other> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] bool operator==(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
return (std::addressof(*lhs) == std::addressof(*rhs));
}
/**
* @brief Compares two handles.
* @tparam Res Type of resource managed by the first handle.
* @tparam Other Type of resource managed by the second handle.
* @tparam Lhs Type of resource managed by the first handle.
* @tparam Rhs Type of resource managed by the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return False if both handles refer to the same registry, true otherwise.
* @return False if both handles refer to the same resource, true otherwise.
*/
template<typename Res, typename Other>
[[nodiscard]] bool operator!=(const resource<Res> &lhs, const resource<Other> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] bool operator!=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
return !(lhs == rhs);
}
/**
* @brief Compares two handles.
* @tparam Res Type of resource managed by the first handle.
* @tparam Other Type of resource managed by the second handle.
* @tparam Lhs Type of resource managed by the first handle.
* @tparam Rhs Type of resource managed by the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return True if the first handle is less than the second, false otherwise.
*/
template<typename Res, typename Other>
[[nodiscard]] bool operator<(const resource<Res> &lhs, const resource<Other> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] bool operator<(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
return (std::addressof(*lhs) < std::addressof(*rhs));
}
/**
* @brief Compares two handles.
* @tparam Res Type of resource managed by the first handle.
* @tparam Other Type of resource managed by the second handle.
* @tparam Lhs Type of resource managed by the first handle.
* @tparam Rhs Type of resource managed by the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return True if the first handle is greater than the second, false otherwise.
*/
template<typename Res, typename Other>
[[nodiscard]] bool operator>(const resource<Res> &lhs, const resource<Other> &rhs) noexcept {
return (std::addressof(*lhs) > std::addressof(*rhs));
template<typename Lhs, typename Rhs>
[[nodiscard]] bool operator>(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
return rhs < lhs;
}
/**
* @brief Compares two handles.
* @tparam Res Type of resource managed by the first handle.
* @tparam Other Type of resource managed by the second handle.
* @tparam Lhs Type of resource managed by the first handle.
* @tparam Rhs Type of resource managed by the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return True if the first handle is less than or equal to the second, false
* otherwise.
*/
template<typename Res, typename Other>
[[nodiscard]] bool operator<=(const resource<Res> &lhs, const resource<Other> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] bool operator<=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
return !(lhs > rhs);
}
/**
* @brief Compares two handles.
* @tparam Res Type of resource managed by the first handle.
* @tparam Other Type of resource managed by the second handle.
* @tparam Lhs Type of resource managed by the first handle.
* @tparam Rhs Type of resource managed by the second handle.
* @param lhs A valid handle.
* @param rhs A valid handle.
* @return True if the first handle is greater than or equal to the second,
* false otherwise.
*/
template<typename Res, typename Other>
[[nodiscard]] bool operator>=(const resource<Res> &lhs, const resource<Other> &rhs) noexcept {
template<typename Lhs, typename Rhs>
[[nodiscard]] bool operator>=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept {
return !(lhs < rhs);
}

View File

@ -76,7 +76,13 @@ class delegate<Ret(Args...)> {
[[nodiscard]] auto wrap(std::index_sequence<Index...>) noexcept {
return [](const void *, Args... args) -> Ret {
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), type_list_element_t<Index, type_list<Args...>>...>) {
return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
} else {
constexpr auto offset = sizeof...(Args) - sizeof...(Index);
return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
}
};
}
@ -85,7 +91,13 @@ class delegate<Ret(Args...)> {
return [](const void *payload, Args... args) -> Ret {
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, type_list_element_t<Index, type_list<Args...>>...>) {
return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
} else {
constexpr auto offset = sizeof...(Args) - sizeof...(Index);
return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
}
};
}
@ -94,7 +106,13 @@ class delegate<Ret(Args...)> {
return [](const void *payload, Args... args) -> Ret {
[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, type_list_element_t<Index, type_list<Args...>>...>) {
return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...));
} else {
constexpr auto offset = sizeof...(Args) - sizeof...(Index);
return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...));
}
};
}
@ -233,6 +251,14 @@ public:
fn = nullptr;
}
/**
* @brief Returns a pointer to the stored callable function target, if any.
* @return An opaque pointer to the stored callable function target.
*/
[[nodiscard]] function_type *target() const noexcept {
return fn;
}
/**
* @brief Returns the instance or the payload linked to a delegate, if any.
* @return An opaque pointer to the underlying data.

View File

@ -75,7 +75,7 @@ public:
template<typename... Args>
void enqueue(Args &&...args) {
if constexpr(std::is_aggregate_v<Type>) {
if constexpr(std::is_aggregate_v<Type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<Type>)) {
events.push_back(Type{std::forward<Args>(args)...});
} else {
events.emplace_back(std::forward<Args>(args)...);
@ -179,7 +179,9 @@ public:
* @param allocator The allocator to use.
*/
basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) noexcept
: pools{container_type{std::move(other.pools.first()), allocator}, allocator} {}
: pools{container_type{std::move(other.pools.first()), allocator}, allocator} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed");
}
/**
* @brief Move assignment operator.
@ -187,6 +189,8 @@ public:
* @return This dispatcher.
*/
basic_dispatcher &operator=(basic_dispatcher &&other) noexcept {
ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed");
pools = std::move(other.pools);
return *this;
}

View File

@ -76,7 +76,9 @@ public:
* @param allocator The allocator to use.
*/
emitter(emitter &&other, const allocator_type &allocator) noexcept
: handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} {}
: handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed");
}
/**
* @brief Move assignment operator.
@ -84,6 +86,8 @@ public:
* @return This dispatcher.
*/
emitter &operator=(emitter &&other) noexcept {
ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed");
handlers = std::move(other.handlers);
return *this;
}

View File

@ -1,8 +1,8 @@
#ifndef ENTT_SIGNAL_SIGH_HPP
#define ENTT_SIGNAL_SIGH_HPP
#include <algorithm>
#include <functional>
#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
@ -56,7 +56,8 @@ class sigh<Ret(Args...), Allocator> {
friend class sink<sigh<Ret(Args...), Allocator>>;
using alloc_traits = std::allocator_traits<Allocator>;
using container_type = std::vector<delegate<Ret(Args...)>, typename alloc_traits::template rebind_alloc<delegate<Ret(Args...)>>>;
using delegate_type = delegate<Ret(Args...)>;
using container_type = std::vector<delegate_type, typename alloc_traits::template rebind_alloc<delegate_type>>;
public:
/*! @brief Allocator type. */
@ -168,8 +169,8 @@ public:
* @param args Arguments to use to invoke listeners.
*/
void publish(Args... args) const {
for(auto &&call: std::as_const(calls)) {
call(args...);
for(auto pos = calls.size(); pos; --pos) {
calls[pos - 1u](args...);
}
}
@ -189,24 +190,24 @@ public:
*/
template<typename Func>
void collect(Func func, Args... args) const {
for(auto &&call: calls) {
if constexpr(std::is_void_v<Ret>) {
for(auto pos = calls.size(); pos; --pos) {
if constexpr(std::is_void_v<Ret> || !std::is_invocable_v<Func, Ret>) {
calls[pos - 1u](args...);
if constexpr(std::is_invocable_r_v<bool, Func>) {
call(args...);
if(func()) {
break;
}
} else {
call(args...);
func();
}
} else {
if constexpr(std::is_invocable_r_v<bool, Func, Ret>) {
if(func(call(args...))) {
if(func(calls[pos - 1u](args...))) {
break;
}
} else {
func(call(args...));
func(calls[pos - 1u](args...));
}
}
}
@ -358,6 +359,7 @@ private:
template<typename Ret, typename... Args, typename Allocator>
class sink<sigh<Ret(Args...), Allocator>> {
using signal_type = sigh<Ret(Args...), Allocator>;
using delegate_type = typename signal_type::delegate_type;
using difference_type = typename signal_type::container_type::difference_type;
template<auto Candidate, typename Type>
@ -370,13 +372,14 @@ class sink<sigh<Ret(Args...), Allocator>> {
sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>();
}
auto before(delegate<Ret(Args...)> call) {
const auto &calls = signal->calls;
const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call));
sink other{*this};
other.offset = calls.cend() - it;
return other;
template<typename Func>
void disconnect_if(Func callback) {
for(auto pos = signal->calls.size(); pos; --pos) {
if(auto &elem = signal->calls[pos - 1u]; callback(elem)) {
elem = std::move(signal->calls.back());
signal->calls.pop_back();
}
}
}
public:
@ -385,8 +388,7 @@ public:
* @param ref A valid reference to a signal object.
*/
sink(sigh<Ret(Args...), Allocator> &ref) noexcept
: offset{},
signal{&ref} {}
: signal{&ref} {}
/**
* @brief Returns false if at least a listener is connected to the sink.
@ -396,89 +398,9 @@ public:
return signal->calls.empty();
}
/**
* @brief Returns a sink that connects before a given free function or an
* unbound member.
* @tparam Function A valid free function pointer.
* @return A properly initialized sink object.
*/
template<auto Function>
[[nodiscard]] sink before() {
delegate<Ret(Args...)> call{};
call.template connect<Function>();
return before(std::move(call));
}
/**
* @brief Returns a sink that connects before a free function with payload
* or a bound member.
* @tparam Candidate Member or free function to look for.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid object that fits the purpose.
* @return A properly initialized sink object.
*/
template<auto Candidate, typename Type>
[[nodiscard]] sink before(Type &&value_or_instance) {
delegate<Ret(Args...)> call{};
call.template connect<Candidate>(value_or_instance);
return before(std::move(call));
}
/**
* @brief Returns a sink that connects before a given instance or specific
* payload.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid object that fits the purpose.
* @return A properly initialized sink object.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<std::remove_pointer_t<Type>>, void>, sink>>
[[nodiscard]] sink before(Type &value_or_instance) {
return before(&value_or_instance);
}
/**
* @brief Returns a sink that connects before a given instance or specific
* payload.
* @param value_or_instance A valid pointer that fits the purpose.
* @return A properly initialized sink object.
*/
[[nodiscard]] sink before(const void *value_or_instance) {
sink other{*this};
if(value_or_instance) {
const auto &calls = signal->calls;
const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) {
return delegate.data() == value_or_instance;
});
other.offset = calls.cend() - it;
}
return other;
}
/**
* @brief Returns a sink that connects before anything else.
* @return A properly initialized sink object.
*/
[[nodiscard]] sink before() {
sink other{*this};
other.offset = signal->calls.size();
return other;
}
/**
* @brief Connects a free function (with or without payload), a bound or an
* unbound member to a signal.
*
* The signal isn't responsible for the connected object or the payload, if
* any. Users must guarantee that the lifetime of the instance overcomes the
* one of the signal. On the other side, the signal handler performs
* checks to avoid multiple connections for the same function.<br/>
* When used to connect a free function with payload, its signature must be
* such that the instance is the first argument before the ones used to
* define the signal itself.
*
* @tparam Candidate Function or member to connect to the signal.
* @tparam Type Type of class or type of payload, if any.
* @param value_or_instance A valid object that fits the purpose, if any.
@ -488,9 +410,9 @@ public:
connection connect(Type &&...value_or_instance) {
disconnect<Candidate>(value_or_instance...);
delegate<Ret(Args...)> call{};
delegate_type call{};
call.template connect<Candidate>(value_or_instance...);
signal->calls.insert(signal->calls.end() - offset, std::move(call));
signal->calls.push_back(std::move(call));
delegate<void(void *)> conn{};
conn.template connect<&release<Candidate, Type...>>(value_or_instance...);
@ -506,21 +428,9 @@ public:
*/
template<auto Candidate, typename... Type>
void disconnect(Type &&...value_or_instance) {
auto &calls = signal->calls;
delegate<Ret(Args...)> call{};
delegate_type call{};
call.template connect<Candidate>(value_or_instance...);
calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end());
}
/**
* @brief Disconnects free functions with payload or bound members from a
* signal.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid object that fits the purpose.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<std::remove_pointer_t<Type>>, void>>>
void disconnect(Type &value_or_instance) {
disconnect(&value_or_instance);
disconnect_if([&call](const auto &elem) { return elem == call; });
}
/**
@ -530,9 +440,7 @@ public:
*/
void disconnect(const void *value_or_instance) {
if(value_or_instance) {
auto &calls = signal->calls;
auto predicate = [value_or_instance](const auto &delegate) { return delegate.data() == value_or_instance; };
calls.erase(std::remove_if(calls.begin(), calls.end(), std::move(predicate)), calls.end());
disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; });
}
}
@ -542,7 +450,6 @@ public:
}
private:
difference_type offset;
signal_type *signal;
};

View File

@ -51,7 +51,20 @@ function(SETUP_TARGET TARGET_NAME)
target_compile_options(
${TARGET_NAME}
PRIVATE
/EHsc /W1 /wd4996 /w14800
# vs2017 emits too many false positives for my tastes
$<IF:$<EQUAL:${MSVC_TOOLSET_VERSION},141>, /W1, /W4>
# clang-cl goes a little wrong with some warnings instead
$<$<STREQUAL:"${CMAKE_CXX_COMPILER_ID}","Clang">:
-Wno-deprecated-declarations
-Wno-ignored-qualifiers
-Wno-unknown-warning-option
-Wno-exceptions
-Wno-unused-local-typedef
-Wno-unused-private-field
>
# documentation diagnostic turned on for clang-cl only
$<$<STREQUAL:"${CMAKE_CXX_COMPILER_ID}","Clang">:-Wdocumentation>
/EHsc /wd4324 /wd4996
$<$<CONFIG:Debug>:/Od>
$<$<CONFIG:Release>:/O2>
)
@ -94,21 +107,23 @@ function(SETUP_BASIC_TEST TEST_NAME TEST_SOURCES)
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
endfunction()
function(SETUP_LIB_TEST TEST_NAME)
add_library(_${TEST_NAME} SHARED $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/lib.cpp)
SETUP_TARGET(_${TEST_NAME} ENTT_API_EXPORT)
SETUP_BASIC_TEST(lib_${TEST_NAME} lib/${TEST_NAME}/main.cpp ENTT_API_IMPORT)
target_link_libraries(lib_${TEST_NAME} PRIVATE _${TEST_NAME})
function(SETUP_LIB_SHARED_TEST TEST_NAME SUB_PATH)
set(TARGET_NAME ${TEST_NAME}_${SUB_PATH})
add_library(_${TARGET_NAME} SHARED $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/${SUB_PATH}/lib.cpp)
SETUP_TARGET(_${TARGET_NAME} ENTT_API_EXPORT)
SETUP_BASIC_TEST(lib_${TARGET_NAME} lib/${TEST_NAME}/${SUB_PATH}/main.cpp ENTT_API_IMPORT)
target_link_libraries(lib_${TARGET_NAME} PRIVATE _${TARGET_NAME})
endfunction()
function(SETUP_PLUGIN_TEST TEST_NAME)
add_library(_${TEST_NAME} MODULE $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/plugin.cpp)
SETUP_TARGET(_${TEST_NAME} ${ARGVN})
SETUP_BASIC_TEST(lib_${TEST_NAME} lib/${TEST_NAME}/main.cpp PLUGIN="$<TARGET_FILE:_${TEST_NAME}>" ${ARGVN})
target_include_directories(_${TEST_NAME} PRIVATE ${cr_INCLUDE_DIR})
target_include_directories(lib_${TEST_NAME} PRIVATE ${cr_INCLUDE_DIR})
target_link_libraries(lib_${TEST_NAME} PRIVATE ${CMAKE_DL_LIBS})
add_dependencies(lib_${TEST_NAME} _${TEST_NAME})
function(SETUP_LIB_PLUGIN_TEST TEST_NAME SUB_PATH)
set(TARGET_NAME ${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_include_directories(_${TARGET_NAME} PRIVATE ${cr_INCLUDE_DIR})
target_include_directories(lib_${TARGET_NAME} PRIVATE ${cr_INCLUDE_DIR})
target_link_libraries(lib_${TARGET_NAME} PRIVATE ${CMAKE_DL_LIBS})
add_dependencies(lib_${TARGET_NAME} _${TARGET_NAME})
endfunction()
# Test benchmark
@ -142,19 +157,21 @@ if(ENTT_BUILD_LIB)
set(cr_INCLUDE_DIR ${cr_SOURCE_DIR})
endif()
SETUP_LIB_TEST(dispatcher)
SETUP_LIB_TEST(emitter)
SETUP_LIB_TEST(locator)
SETUP_LIB_TEST(meta)
SETUP_LIB_TEST(registry)
SETUP_LIB_SHARED_TEST(dispatcher shared)
SETUP_LIB_PLUGIN_TEST(dispatcher plugin)
SETUP_PLUGIN_TEST(dispatcher_plugin)
SETUP_PLUGIN_TEST(emitter_plugin)
SETUP_PLUGIN_TEST(locator_plugin)
SETUP_PLUGIN_TEST(meta_plugin)
SETUP_PLUGIN_TEST(registry_plugin)
SETUP_LIB_SHARED_TEST(emitter shared)
SETUP_LIB_PLUGIN_TEST(emitter plugin)
SETUP_PLUGIN_TEST(meta_plugin_std ENTT_STANDARD_CPP)
SETUP_LIB_SHARED_TEST(locator shared)
SETUP_LIB_PLUGIN_TEST(locator plugin)
SETUP_LIB_SHARED_TEST(meta shared)
SETUP_LIB_PLUGIN_TEST(meta plugin)
SETUP_LIB_PLUGIN_TEST(meta plugin_std ENTT_STANDARD_CPP)
SETUP_LIB_SHARED_TEST(registry shared)
SETUP_LIB_PLUGIN_TEST(registry plugin)
endif()
# Test snapshot
@ -215,10 +232,11 @@ SETUP_BASIC_TEST(observer entt/entity/observer.cpp)
SETUP_BASIC_TEST(organizer entt/entity/organizer.cpp)
SETUP_BASIC_TEST(registry entt/entity/registry.cpp)
SETUP_BASIC_TEST(runtime_view entt/entity/runtime_view.cpp)
SETUP_BASIC_TEST(sigh_mixin entt/entity/sigh_mixin.cpp)
SETUP_BASIC_TEST(snapshot entt/entity/snapshot.cpp)
SETUP_BASIC_TEST(sparse_set entt/entity/sparse_set.cpp)
SETUP_BASIC_TEST(storage entt/entity/storage.cpp)
SETUP_BASIC_TEST(storage_mixin entt/entity/storage_mixin.cpp)
SETUP_BASIC_TEST(storage_entity entt/entity/storage_entity.cpp)
SETUP_BASIC_TEST(view entt/entity/view.cpp)
# Test graph

View File

@ -19,7 +19,9 @@ struct stable_position: position {
};
template<auto>
struct comp { int x; };
struct comp {
int x;
};
struct timer final {
timer()
@ -34,16 +36,24 @@ private:
std::chrono::time_point<std::chrono::system_clock> start;
};
template<typename Func, typename... Args>
void generic_with(Func func) {
timer timer;
func();
timer.elapsed();
}
template<typename Iterable, typename Func>
void generic(Iterable &&iterable, Func func) {
void iterate_with(Iterable &&iterable, Func func) {
timer timer;
std::forward<Iterable>(iterable).each(func);
timer.elapsed();
}
template<typename Func>
void pathological(Func func) {
void pathological_with(Func func) {
entt::registry registry;
auto view = func(registry);
for(std::uint64_t i = 0; i < 500000L; i++) {
const auto entity = registry.create();
@ -54,10 +64,21 @@ void pathological(Func func) {
for(auto i = 0; i < 10; ++i) {
registry.each([i = 0, &registry](const auto entity) mutable {
if(!(++i % 7)) { registry.remove<position>(entity); }
if(!(++i % 11)) { registry.remove<velocity>(entity); }
if(!(++i % 13)) { registry.remove<comp<0>>(entity); }
if(!(++i % 17)) { registry.destroy(entity); }
if(!(++i % 7)) {
registry.remove<position>(entity);
}
if(!(++i % 11)) {
registry.remove<velocity>(entity);
}
if(!(++i % 13)) {
registry.remove<comp<0>>(entity);
}
if(!(++i % 17)) {
registry.destroy(entity);
}
});
for(std::uint64_t j = 0; j < 50000L; j++) {
@ -69,7 +90,7 @@ void pathological(Func func) {
}
timer timer;
func(registry).each([](auto &...comp) { ((comp.x = {}), ...); });
view.each([](auto &...comp) { ((comp.x = {}), ...); });
timer.elapsed();
}
@ -78,13 +99,11 @@ TEST(Benchmark, Create) {
std::cout << "Creating 1000000 entities" << std::endl;
timer timer;
for(std::uint64_t i = 0; i < 1000000L; i++) {
static_cast<void>(registry.create());
}
timer.elapsed();
generic_with([&]() {
for(std::uint64_t i = 0; i < 1000000L; i++) {
static_cast<void>(registry.create());
}
});
}
TEST(Benchmark, CreateMany) {
@ -93,9 +112,9 @@ TEST(Benchmark, CreateMany) {
std::cout << "Creating 1000000 entities at once" << std::endl;
timer timer;
registry.create(entities.begin(), entities.end());
timer.elapsed();
generic_with([&]() {
registry.create(entities.begin(), entities.end());
});
}
TEST(Benchmark, CreateManyAndEmplaceComponents) {
@ -104,15 +123,14 @@ TEST(Benchmark, CreateManyAndEmplaceComponents) {
std::cout << "Creating 1000000 entities at once and emplace components" << std::endl;
timer timer;
registry.create(entities.begin(), entities.end());
generic_with([&]() {
registry.create(entities.begin(), entities.end());
for(const auto entity: entities) {
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
}
timer.elapsed();
for(const auto entity: entities) {
registry.emplace<position>(entity);
registry.emplace<velocity>(entity);
}
});
}
TEST(Benchmark, CreateManyWithComponents) {
@ -121,79 +139,107 @@ TEST(Benchmark, CreateManyWithComponents) {
std::cout << "Creating 1000000 entities at once with components" << std::endl;
timer timer;
registry.create(entities.begin(), entities.end());
registry.insert<position>(entities.begin(), entities.end());
registry.insert<velocity>(entities.begin(), entities.end());
timer.elapsed();
generic_with([&]() {
registry.create(entities.begin(), entities.end());
registry.insert<position>(entities.begin(), entities.end());
registry.insert<velocity>(entities.begin(), entities.end());
});
}
TEST(Benchmark, Erase) {
entt::registry registry;
std::vector<entt::entity> entities(1000000);
auto view = registry.view<int>();
auto view = registry.view<position>();
std::cout << "Erasing 1000000 components from their entities" << std::endl;
registry.create(entities.begin(), entities.end());
registry.insert<int>(entities.begin(), entities.end());
registry.insert<position>(entities.begin(), entities.end());
timer timer;
for(auto entity: view) {
registry.erase<int>(entity);
}
timer.elapsed();
generic_with([&]() {
for(auto entity: view) {
registry.erase<position>(entity);
}
});
}
TEST(Benchmark, EraseMany) {
entt::registry registry;
std::vector<entt::entity> entities(1000000);
auto view = registry.view<int>();
auto view = registry.view<position>();
std::cout << "Erasing 1000000 components from their entities at once" << std::endl;
registry.create(entities.begin(), entities.end());
registry.insert<int>(entities.begin(), entities.end());
registry.insert<position>(entities.begin(), entities.end());
timer timer;
registry.erase<int>(view.begin(), view.end());
timer.elapsed();
generic_with([&]() {
registry.erase<position>(view.begin(), view.end());
});
}
TEST(Benchmark, EraseManyMulti) {
entt::registry registry;
std::vector<entt::entity> entities(1000000);
auto view = registry.view<position>();
std::cout << "Erasing 1000000 components per type from their entities at once" << std::endl;
registry.create(entities.begin(), entities.end());
registry.insert<position>(entities.begin(), entities.end());
registry.insert<velocity>(entities.begin(), entities.end());
generic_with([&]() {
registry.erase<position, velocity>(view.begin(), view.end());
});
}
TEST(Benchmark, Remove) {
entt::registry registry;
std::vector<entt::entity> entities(1000000);
auto view = registry.view<int>();
auto view = registry.view<position>();
std::cout << "Removing 1000000 components from their entities" << std::endl;
registry.create(entities.begin(), entities.end());
registry.insert<int>(entities.begin(), entities.end());
registry.insert<position>(entities.begin(), entities.end());
timer timer;
for(auto entity: view) {
registry.remove<int>(entity);
}
timer.elapsed();
generic_with([&]() {
for(auto entity: view) {
registry.remove<position>(entity);
}
});
}
TEST(Benchmark, RemoveMany) {
entt::registry registry;
std::vector<entt::entity> entities(1000000);
auto view = registry.view<int>();
auto view = registry.view<position>();
std::cout << "Removing 1000000 components from their entities at once" << std::endl;
registry.create(entities.begin(), entities.end());
registry.insert<int>(entities.begin(), entities.end());
registry.insert<position>(entities.begin(), entities.end());
timer timer;
registry.remove<int>(view.begin(), view.end());
timer.elapsed();
generic_with([&]() {
registry.remove<position>(view.begin(), view.end());
});
}
TEST(Benchmark, RemoveManyMulti) {
entt::registry registry;
std::vector<entt::entity> entities(1000000);
auto view = registry.view<position>();
std::cout << "Removing 1000000 components per type from their entities at once" << std::endl;
registry.create(entities.begin(), entities.end());
registry.insert<position>(entities.begin(), entities.end());
registry.insert<velocity>(entities.begin(), entities.end());
generic_with([&]() {
registry.remove<position, velocity>(view.begin(), view.end());
});
}
TEST(Benchmark, Clear) {
@ -203,11 +249,40 @@ TEST(Benchmark, Clear) {
std::cout << "Clearing 1000000 components from their entities" << std::endl;
registry.create(entities.begin(), entities.end());
registry.insert<int>(entities.begin(), entities.end());
registry.insert<position>(entities.begin(), entities.end());
timer timer;
registry.clear<int>();
timer.elapsed();
generic_with([&]() {
registry.clear<position>();
});
}
TEST(Benchmark, ClearMulti) {
entt::registry registry;
std::vector<entt::entity> entities(1000000);
std::cout << "Clearing 1000000 components per type from their entities" << std::endl;
registry.create(entities.begin(), entities.end());
registry.insert<position>(entities.begin(), entities.end());
registry.insert<velocity>(entities.begin(), entities.end());
generic_with([&]() {
registry.clear<position, velocity>();
});
}
TEST(Benchmark, ClearStable) {
entt::registry registry;
std::vector<entt::entity> entities(1000000);
std::cout << "Clearing 1000000 stable components from their entities" << std::endl;
registry.create(entities.begin(), entities.end());
registry.insert<stable_position>(entities.begin(), entities.end());
generic_with([&]() {
registry.clear<stable_position>();
});
}
TEST(Benchmark, Recycle) {
@ -217,18 +292,13 @@ TEST(Benchmark, Recycle) {
std::cout << "Recycling 1000000 entities" << std::endl;
registry.create(entities.begin(), entities.end());
registry.destroy(entities.begin(), entities.end());
registry.each([&registry](auto entity) {
registry.destroy(entity);
generic_with([&]() {
for(auto next = entities.size(); next; --next) {
entities[next] = registry.create();
}
});
timer timer;
for(auto next = entities.size(); next; --next) {
static_cast<void>(registry.create());
}
timer.elapsed();
}
TEST(Benchmark, RecycleMany) {
@ -238,62 +308,121 @@ TEST(Benchmark, RecycleMany) {
std::cout << "Recycling 1000000 entities" << std::endl;
registry.create(entities.begin(), entities.end());
registry.destroy(entities.begin(), entities.end());
registry.each([&registry](auto entity) {
registry.destroy(entity);
generic_with([&]() {
registry.create(entities.begin(), entities.end());
});
timer timer;
registry.create(entities.begin(), entities.end());
timer.elapsed();
}
TEST(Benchmark, Destroy) {
entt::registry registry;
std::vector<entt::entity> entities(1000000);
auto view = registry.view<int>();
auto view = registry.view<position>();
std::cout << "Destroying 1000000 entities" << std::endl;
registry.create(entities.begin(), entities.end());
registry.insert<int>(entities.begin(), entities.end());
registry.insert<position>(entities.begin(), entities.end());
timer timer;
for(auto entity: view) {
registry.destroy(entity);
}
timer.elapsed();
generic_with([&]() {
for(auto entity: view) {
registry.destroy(entity);
}
});
}
TEST(Benchmark, DestroyMany) {
entt::registry registry;
std::vector<entt::entity> entities(1000000);
auto view = registry.view<int>();
auto view = registry.view<position>();
std::cout << "Destroying 1000000 entities at once" << std::endl;
registry.create(entities.begin(), entities.end());
registry.insert<int>(entities.begin(), entities.end());
registry.insert<position>(entities.begin(), entities.end());
timer timer;
registry.destroy(view.begin(), view.end());
timer.elapsed();
generic_with([&]() {
registry.destroy(view.begin(), view.end());
});
}
TEST(Benchmark, DestroyManyFastPath) {
TEST(Benchmark, DestroyManyMulti) {
entt::registry registry;
std::vector<entt::entity> entities(1000000);
auto view = registry.view<position>();
std::cout << "Destroying 1000000 entities at once, multiple components" << std::endl;
registry.create(entities.begin(), entities.end());
registry.insert<position>(entities.begin(), entities.end());
registry.insert<velocity>(entities.begin(), entities.end());
generic_with([&]() {
registry.destroy(view.begin(), view.end());
});
}
TEST(Benchmark, GetFromRegistry) {
entt::registry registry;
std::vector<entt::entity> entities(1000000);
std::cout << "Destroying 1000000 entities at once, fast path" << std::endl;
registry.create(entities.begin(), entities.end());
registry.insert<position>(entities.begin(), entities.end());
generic_with([&]() {
for(auto entity: entities) {
registry.get<position>(entity).x = 0u;
}
});
}
TEST(Benchmark, GetFromRegistryMulti) {
entt::registry registry;
std::vector<entt::entity> entities(1000000);
registry.create(entities.begin(), entities.end());
registry.insert<int>(entities.begin(), entities.end());
registry.insert<position>(entities.begin(), entities.end());
registry.insert<velocity>(entities.begin(), entities.end());
timer timer;
registry.destroy(entities.begin(), entities.end());
timer.elapsed();
generic_with([&]() {
for(auto entity: entities) {
registry.get<position>(entity).x = 0u;
registry.get<velocity>(entity).y = 0u;
}
});
}
TEST(Benchmark, GetFromView) {
entt::registry registry;
std::vector<entt::entity> entities(1000000);
auto view = registry.view<position>();
registry.create(entities.begin(), entities.end());
registry.insert<position>(entities.begin(), entities.end());
generic_with([&]() {
for(auto entity: entities) {
view.get<position>(entity).x = 0u;
}
});
}
TEST(Benchmark, GetFromViewMulti) {
entt::registry registry;
std::vector<entt::entity> entities(1000000);
auto view = registry.view<position, velocity>();
registry.create(entities.begin(), entities.end());
registry.insert<position>(entities.begin(), entities.end());
registry.insert<velocity>(entities.begin(), entities.end());
generic_with([&]() {
for(auto entity: entities) {
view.get<position>(entity).x = 0u;
view.get<velocity>(entity).y = 0u;
}
});
}
TEST(Benchmark, IterateSingleComponent1M) {
@ -306,22 +435,22 @@ TEST(Benchmark, IterateSingleComponent1M) {
registry.emplace<position>(entity);
}
generic(registry.view<position>(), [](auto &...comp) {
iterate_with(registry.view<position>(), [](auto &...comp) {
((comp.x = {}), ...);
});
}
TEST(Benchmark, IterateSingleComponentTombstonePolicy1M) {
TEST(Benchmark, IterateSingleStableComponent1M) {
entt::registry registry;
std::cout << "Iterating over 1000000 entities, one component, tombstone policy" << std::endl;
std::cout << "Iterating over 1000000 entities, one stable component" << std::endl;
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
registry.emplace<stable_position>(entity);
}
generic(registry.view<stable_position>(), [](auto &...comp) {
iterate_with(registry.view<stable_position>(), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -339,7 +468,7 @@ TEST(Benchmark, IterateSingleComponentRuntime1M) {
entt::runtime_view view{};
view.iterate(registry.storage<position>());
generic(view, [&registry](auto entity) {
iterate_with(view, [&](auto entity) {
registry.get<position>(entity).x = {};
});
}
@ -355,15 +484,15 @@ TEST(Benchmark, IterateTwoComponents1M) {
registry.emplace<velocity>(entity);
}
generic(registry.view<position, velocity>(), [](auto &...comp) {
iterate_with(registry.view<position, velocity>(), [](auto &...comp) {
((comp.x = {}), ...);
});
}
TEST(Benchmark, IterateTombstonePolicyTwoComponentsTombstonePolicy1M) {
TEST(Benchmark, IterateTwoStableComponents1M) {
entt::registry registry;
std::cout << "Iterating over 1000000 entities, two components, tombstone policy" << std::endl;
std::cout << "Iterating over 1000000 entities, two stable components" << std::endl;
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
@ -371,7 +500,7 @@ TEST(Benchmark, IterateTombstonePolicyTwoComponentsTombstonePolicy1M) {
registry.emplace<velocity>(entity);
}
generic(registry.view<stable_position, velocity>(), [](auto &...comp) {
iterate_with(registry.view<stable_position, velocity>(), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -390,7 +519,7 @@ TEST(Benchmark, IterateTwoComponents1MHalf) {
}
}
generic(registry.view<position, velocity>(), [](auto &...comp) {
iterate_with(registry.view<position, velocity>(), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -409,7 +538,7 @@ TEST(Benchmark, IterateTwoComponents1MOne) {
}
}
generic(registry.view<position, velocity>(), [](auto &...comp) {
iterate_with(registry.view<position, velocity>(), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -425,7 +554,7 @@ TEST(Benchmark, IterateTwoComponentsNonOwningGroup1M) {
registry.emplace<velocity>(entity);
}
generic(registry.group<>(entt::get<position, velocity>), [](auto &...comp) {
iterate_with(registry.group<>(entt::get<position, velocity>), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -441,7 +570,7 @@ TEST(Benchmark, IterateTwoComponentsFullOwningGroup1M) {
registry.emplace<velocity>(entity);
}
generic(registry.group<position, velocity>(), [](auto &...comp) {
iterate_with(registry.group<position, velocity>(), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -457,7 +586,7 @@ TEST(Benchmark, IterateTwoComponentsPartialOwningGroup1M) {
registry.emplace<velocity>(entity);
}
generic(registry.group<position>(entt::get<velocity>), [](auto &...comp) {
iterate_with(registry.group<position>(entt::get<velocity>), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -477,7 +606,7 @@ TEST(Benchmark, IterateTwoComponentsRuntime1M) {
view.iterate(registry.storage<position>())
.iterate(registry.storage<velocity>());
generic(view, [&registry](auto entity) {
iterate_with(view, [&](auto entity) {
registry.get<position>(entity).x = {};
registry.get<velocity>(entity).x = {};
});
@ -501,7 +630,7 @@ TEST(Benchmark, IterateTwoComponentsRuntime1MHalf) {
view.iterate(registry.storage<position>())
.iterate(registry.storage<velocity>());
generic(view, [&registry](auto entity) {
iterate_with(view, [&](auto entity) {
registry.get<position>(entity).x = {};
registry.get<velocity>(entity).x = {};
});
@ -525,7 +654,7 @@ TEST(Benchmark, IterateTwoComponentsRuntime1MOne) {
view.iterate(registry.storage<position>())
.iterate(registry.storage<velocity>());
generic(view, [&registry](auto entity) {
iterate_with(view, [&](auto entity) {
registry.get<position>(entity).x = {};
registry.get<velocity>(entity).x = {};
});
@ -543,15 +672,15 @@ TEST(Benchmark, IterateThreeComponents1M) {
registry.emplace<comp<0>>(entity);
}
generic(registry.view<position, velocity, comp<0>>(), [](auto &...comp) {
iterate_with(registry.view<position, velocity, comp<0>>(), [](auto &...comp) {
((comp.x = {}), ...);
});
}
TEST(Benchmark, IterateThreeComponentsTombstonePolicy1M) {
TEST(Benchmark, IterateThreeStableComponents1M) {
entt::registry registry;
std::cout << "Iterating over 1000000 entities, three components, tombstone policy" << std::endl;
std::cout << "Iterating over 1000000 entities, three stable components" << std::endl;
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
@ -560,7 +689,7 @@ TEST(Benchmark, IterateThreeComponentsTombstonePolicy1M) {
registry.emplace<comp<0>>(entity);
}
generic(registry.view<stable_position, velocity, comp<0>>(), [](auto &...comp) {
iterate_with(registry.view<stable_position, velocity, comp<0>>(), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -580,7 +709,7 @@ TEST(Benchmark, IterateThreeComponents1MHalf) {
}
}
generic(registry.view<position, velocity, comp<0>>(), [](auto &...comp) {
iterate_with(registry.view<position, velocity, comp<0>>(), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -600,7 +729,7 @@ TEST(Benchmark, IterateThreeComponents1MOne) {
}
}
generic(registry.view<position, velocity, comp<0>>(), [](auto &...comp) {
iterate_with(registry.view<position, velocity, comp<0>>(), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -617,7 +746,7 @@ TEST(Benchmark, IterateThreeComponentsNonOwningGroup1M) {
registry.emplace<comp<0>>(entity);
}
generic(registry.group<>(entt::get<position, velocity, comp<0>>), [](auto &...comp) {
iterate_with(registry.group<>(entt::get<position, velocity, comp<0>>), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -634,7 +763,7 @@ TEST(Benchmark, IterateThreeComponentsFullOwningGroup1M) {
registry.emplace<comp<0>>(entity);
}
generic(registry.group<position, velocity, comp<0>>(), [](auto &...comp) {
iterate_with(registry.group<position, velocity, comp<0>>(), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -651,7 +780,7 @@ TEST(Benchmark, IterateThreeComponentsPartialOwningGroup1M) {
registry.emplace<comp<0>>(entity);
}
generic(registry.group<position, velocity>(entt::get<comp<0>>), [](auto &...comp) {
iterate_with(registry.group<position, velocity>(entt::get<comp<0>>), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -673,7 +802,7 @@ TEST(Benchmark, IterateThreeComponentsRuntime1M) {
.iterate(registry.storage<velocity>())
.iterate(registry.storage<comp<0>>());
generic(view, [&registry](auto entity) {
iterate_with(view, [&](auto entity) {
registry.get<position>(entity).x = {};
registry.get<velocity>(entity).x = {};
registry.get<comp<0>>(entity).x = {};
@ -700,7 +829,7 @@ TEST(Benchmark, IterateThreeComponentsRuntime1MHalf) {
.iterate(registry.storage<velocity>())
.iterate(registry.storage<comp<0>>());
generic(view, [&registry](auto entity) {
iterate_with(view, [&](auto entity) {
registry.get<position>(entity).x = {};
registry.get<velocity>(entity).x = {};
registry.get<comp<0>>(entity).x = {};
@ -727,7 +856,7 @@ TEST(Benchmark, IterateThreeComponentsRuntime1MOne) {
.iterate(registry.storage<velocity>())
.iterate(registry.storage<comp<0>>());
generic(view, [&registry](auto entity) {
iterate_with(view, [&](auto entity) {
registry.get<position>(entity).x = {};
registry.get<velocity>(entity).x = {};
registry.get<comp<0>>(entity).x = {};
@ -748,15 +877,15 @@ TEST(Benchmark, IterateFiveComponents1M) {
registry.emplace<comp<2>>(entity);
}
generic(registry.view<position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) {
iterate_with(registry.view<position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) {
((comp.x = {}), ...);
});
}
TEST(Benchmark, IterateFiveComponentsTombstonePolicy1M) {
TEST(Benchmark, IterateFiveStableComponents1M) {
entt::registry registry;
std::cout << "Iterating over 1000000 entities, five components, tombstone policy" << std::endl;
std::cout << "Iterating over 1000000 entities, five stable components" << std::endl;
for(std::uint64_t i = 0; i < 1000000L; i++) {
const auto entity = registry.create();
@ -767,7 +896,7 @@ TEST(Benchmark, IterateFiveComponentsTombstonePolicy1M) {
registry.emplace<comp<2>>(entity);
}
generic(registry.view<stable_position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) {
iterate_with(registry.view<stable_position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -789,7 +918,7 @@ TEST(Benchmark, IterateFiveComponents1MHalf) {
}
}
generic(registry.view<position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) {
iterate_with(registry.view<position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -811,7 +940,7 @@ TEST(Benchmark, IterateFiveComponents1MOne) {
}
}
generic(registry.view<position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) {
iterate_with(registry.view<position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -830,7 +959,7 @@ TEST(Benchmark, IterateFiveComponentsNonOwningGroup1M) {
registry.emplace<comp<2>>(entity);
}
generic(registry.group<>(entt::get<position, velocity, comp<0>, comp<1>, comp<2>>), [](auto &...comp) {
iterate_with(registry.group<>(entt::get<position, velocity, comp<0>, comp<1>, comp<2>>), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -849,7 +978,7 @@ TEST(Benchmark, IterateFiveComponentsFullOwningGroup1M) {
registry.emplace<comp<2>>(entity);
}
generic(registry.group<position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) {
iterate_with(registry.group<position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -868,7 +997,7 @@ TEST(Benchmark, IterateFiveComponentsPartialFourOfFiveOwningGroup1M) {
registry.emplace<comp<2>>(entity);
}
generic(registry.group<position, velocity, comp<0>, comp<1>>(entt::get<comp<2>>), [](auto &...comp) {
iterate_with(registry.group<position, velocity, comp<0>, comp<1>>(entt::get<comp<2>>), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -887,7 +1016,7 @@ TEST(Benchmark, IterateFiveComponentsPartialThreeOfFiveOwningGroup1M) {
registry.emplace<comp<2>>(entity);
}
generic(registry.group<position, velocity, comp<0>>(entt::get<comp<1>, comp<2>>), [](auto &...comp) {
iterate_with(registry.group<position, velocity, comp<0>>(entt::get<comp<1>, comp<2>>), [](auto &...comp) {
((comp.x = {}), ...);
});
}
@ -913,7 +1042,7 @@ TEST(Benchmark, IterateFiveComponentsRuntime1M) {
.iterate(registry.storage<comp<1>>())
.iterate(registry.storage<comp<2>>());
generic(view, [&registry](auto entity) {
iterate_with(view, [&](auto entity) {
registry.get<position>(entity).x = {};
registry.get<velocity>(entity).x = {};
registry.get<comp<0>>(entity).x = {};
@ -946,7 +1075,7 @@ TEST(Benchmark, IterateFiveComponentsRuntime1MHalf) {
.iterate(registry.storage<comp<1>>())
.iterate(registry.storage<comp<2>>());
generic(view, [&registry](auto entity) {
iterate_with(view, [&](auto entity) {
registry.get<position>(entity).x = {};
registry.get<velocity>(entity).x = {};
registry.get<comp<0>>(entity).x = {};
@ -979,7 +1108,7 @@ TEST(Benchmark, IterateFiveComponentsRuntime1MOne) {
.iterate(registry.storage<comp<1>>())
.iterate(registry.storage<comp<2>>());
generic(view, [&registry](auto entity) {
iterate_with(view, [&](auto entity) {
registry.get<position>(entity).x = {};
registry.get<velocity>(entity).x = {};
registry.get<comp<0>>(entity).x = {};
@ -990,22 +1119,22 @@ TEST(Benchmark, IterateFiveComponentsRuntime1MOne) {
TEST(Benchmark, IteratePathological) {
std::cout << "Pathological case" << std::endl;
pathological([](auto &registry) { return registry.template view<position, velocity, comp<0>>(); });
pathological_with([](auto &registry) { return registry.template view<position, velocity, comp<0>>(); });
}
TEST(Benchmark, IteratePathologicalNonOwningGroup) {
std::cout << "Pathological case (non-owning group)" << std::endl;
pathological([](auto &registry) { return registry.template group<>(entt::get<position, velocity, comp<0>>); });
pathological_with([](auto &registry) { return registry.template group<>(entt::get<position, velocity, comp<0>>); });
}
TEST(Benchmark, IteratePathologicalFullOwningGroup) {
std::cout << "Pathological case (full-owning group)" << std::endl;
pathological([](auto &registry) { return registry.template group<position, velocity, comp<0>>(); });
pathological_with([](auto &registry) { return registry.template group<position, velocity, comp<0>>(); });
}
TEST(Benchmark, IteratePathologicalPartialOwningGroup) {
std::cout << "Pathological case (partial-owning group)" << std::endl;
pathological([](auto &registry) { return registry.template group<position, velocity>(entt::get<comp<0>>); });
pathological_with([](auto &registry) { return registry.template group<position, velocity>(entt::get<comp<0>>); });
}
TEST(Benchmark, SortSingle) {
@ -1018,9 +1147,9 @@ TEST(Benchmark, SortSingle) {
registry.emplace<position>(entity, i, i);
}
timer timer;
registry.sort<position>([](const auto &lhs, const auto &rhs) { return lhs.x < rhs.x && lhs.y < rhs.y; });
timer.elapsed();
generic_with([&]() {
registry.sort<position>([](const auto &lhs, const auto &rhs) { return lhs.x < rhs.x && lhs.y < rhs.y; });
});
}
TEST(Benchmark, SortMulti) {
@ -1036,9 +1165,9 @@ TEST(Benchmark, SortMulti) {
registry.sort<position>([](const auto &lhs, const auto &rhs) { return lhs.x < rhs.x && lhs.y < rhs.y; });
timer timer;
registry.sort<velocity, position>();
timer.elapsed();
generic_with([&]() {
registry.sort<velocity, position>();
});
}
TEST(Benchmark, AlmostSortedStdSort) {
@ -1062,9 +1191,9 @@ TEST(Benchmark, AlmostSortedStdSort) {
registry.emplace<position>(entity, 50000 * i, 50000 * i);
}
timer timer;
registry.sort<position>([](const auto &lhs, const auto &rhs) { return lhs.x > rhs.x && lhs.y > rhs.y; });
timer.elapsed();
generic_with([&]() {
registry.sort<position>([](const auto &lhs, const auto &rhs) { return lhs.x > rhs.x && lhs.y > rhs.y; });
});
}
TEST(Benchmark, AlmostSortedInsertionSort) {
@ -1088,7 +1217,7 @@ TEST(Benchmark, AlmostSortedInsertionSort) {
registry.emplace<position>(entity, 50000 * i, 50000 * i);
}
timer timer;
registry.sort<position>([](const auto &lhs, const auto &rhs) { return lhs.x > rhs.x && lhs.y > rhs.y; }, entt::insertion_sort{});
timer.elapsed();
generic_with([&]() {
registry.sort<position>([](const auto &lhs, const auto &rhs) { return lhs.x > rhs.x && lhs.y > rhs.y; }, entt::insertion_sort{});
});
}

View File

@ -22,7 +22,7 @@ struct basic_test_allocator: std::allocator<Type> {
return *this;
}
bool operator==(const basic_test_allocator &other) {
bool operator==(const basic_test_allocator &other) const {
return (this == &other);
}
};

View File

@ -25,14 +25,14 @@ public:
using propagate_on_container_swap = std::true_type;
using exception_type = test_exception;
template<class Other>
template<typename Other>
struct rebind {
using other = throwing_allocator<Other>;
};
throwing_allocator() = default;
template<class Other>
template<typename Other>
throwing_allocator(const throwing_allocator<Other> &other)
: base{other} {}

View File

@ -100,7 +100,7 @@ TEST(DenseMap, Functionalities) {
}
TEST(DenseMap, Constructors) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_map<int, int> map;
ASSERT_EQ(map.bucket_count(), minimum_bucket_count);
@ -395,7 +395,7 @@ TEST(DenseMap, Insert) {
}
TEST(DenseMap, InsertRehash) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_map<std::size_t, std::size_t, entt::identity> map;
ASSERT_EQ(map.size(), 0u);
@ -429,7 +429,7 @@ TEST(DenseMap, InsertRehash) {
}
TEST(DenseMap, InsertSameBucket) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_map<std::size_t, std::size_t, entt::identity> map;
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
@ -598,7 +598,7 @@ TEST(DenseMap, Emplace) {
}
TEST(DenseMap, EmplaceRehash) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_map<std::size_t, std::size_t, entt::identity> map;
ASSERT_EQ(map.size(), 0u);
@ -633,7 +633,7 @@ TEST(DenseMap, EmplaceRehash) {
}
TEST(DenseMap, EmplaceSameBucket) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_map<std::size_t, std::size_t, entt::identity> map;
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
@ -681,7 +681,7 @@ TEST(DenseMap, TryEmplace) {
}
TEST(DenseMap, TryEmplaceRehash) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_map<std::size_t, std::size_t, entt::identity> map;
ASSERT_EQ(map.size(), 0u);
@ -715,7 +715,7 @@ TEST(DenseMap, TryEmplaceRehash) {
}
TEST(DenseMap, TryEmplaceSameBucket) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_map<std::size_t, std::size_t, entt::identity> map;
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
@ -749,7 +749,7 @@ TEST(DenseMap, TryEmplaceMovableType) {
}
TEST(DenseMap, Erase) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_map<std::size_t, std::size_t, entt::identity> map;
for(std::size_t next{}, last = minimum_bucket_count + 1u; next < last; ++next) {
@ -799,7 +799,7 @@ TEST(DenseMap, Erase) {
}
TEST(DenseMap, EraseWithMovableKeyValue) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_map<std::string, std::size_t> map;
map.emplace("0", 0u);
@ -817,7 +817,7 @@ TEST(DenseMap, EraseWithMovableKeyValue) {
}
TEST(DenseMap, EraseFromBucket) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_map<std::size_t, std::size_t, entt::identity> map;
ASSERT_EQ(map.bucket_count(), minimum_bucket_count);
@ -995,7 +995,7 @@ TEST(DenseMap, LocalIterator) {
static_assert(std::is_same_v<iterator::pointer, entt::input_iterator_pointer<std::pair<const std::size_t &, std::size_t &>>>);
static_assert(std::is_same_v<iterator::reference, std::pair<const std::size_t &, std::size_t &>>);
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_map<std::size_t, std::size_t, entt::identity> map;
map.emplace(3u, 42u);
map.emplace(3u + minimum_bucket_count, 99u);
@ -1023,7 +1023,7 @@ TEST(DenseMap, ConstLocalIterator) {
static_assert(std::is_same_v<iterator::pointer, entt::input_iterator_pointer<std::pair<const std::size_t &, const std::size_t &>>>);
static_assert(std::is_same_v<iterator::reference, std::pair<const std::size_t &, const std::size_t &>>);
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_map<std::size_t, std::size_t, entt::identity> map;
map.emplace(3u, 42u);
map.emplace(3u + minimum_bucket_count, 99u);
@ -1064,7 +1064,7 @@ TEST(DenseMap, LocalIteratorConversion) {
}
TEST(DenseMap, Rehash) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_map<std::size_t, std::size_t, entt::identity> map;
map[32u] = 99u;
@ -1147,7 +1147,7 @@ TEST(DenseMap, Rehash) {
}
TEST(DenseMap, Reserve) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_map<int, int> map;
ASSERT_EQ(map.bucket_count(), minimum_bucket_count);
@ -1159,7 +1159,7 @@ TEST(DenseMap, Reserve) {
map.reserve(minimum_bucket_count);
ASSERT_EQ(map.bucket_count(), 2 * minimum_bucket_count);
ASSERT_EQ(map.bucket_count(), entt::next_power_of_two(std::ceil(minimum_bucket_count / map.max_load_factor())));
ASSERT_EQ(map.bucket_count(), entt::next_power_of_two(static_cast<std::size_t>(std::ceil(minimum_bucket_count / map.max_load_factor()))));
}
TEST(DenseMap, ThrowingAllocator) {
@ -1167,7 +1167,7 @@ TEST(DenseMap, ThrowingAllocator) {
using packed_allocator = test::throwing_allocator<entt::internal::dense_map_node<std::size_t, std::size_t>>;
using packed_exception = typename packed_allocator::exception_type;
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_map<std::size_t, std::size_t, std::hash<std::size_t>, std::equal_to<std::size_t>, allocator> map{};
packed_allocator::trigger_on_allocate = true;

View File

@ -98,7 +98,7 @@ TEST(DenseSet, Functionalities) {
}
TEST(DenseSet, Constructors) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<int> set;
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
@ -357,7 +357,7 @@ TEST(DenseSet, Insert) {
}
TEST(DenseSet, InsertRehash) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
ASSERT_EQ(set.size(), 0u);
@ -388,7 +388,7 @@ TEST(DenseSet, InsertRehash) {
}
TEST(DenseSet, InsertSameBucket) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
@ -451,7 +451,7 @@ TEST(DenseSet, Emplace) {
}
TEST(DenseSet, EmplaceRehash) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
ASSERT_EQ(set.size(), 0u);
@ -483,7 +483,7 @@ TEST(DenseSet, EmplaceRehash) {
}
TEST(DenseSet, EmplaceSameBucket) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
for(std::size_t next{}; next < minimum_bucket_count; ++next) {
@ -503,7 +503,7 @@ TEST(DenseSet, EmplaceSameBucket) {
}
TEST(DenseSet, Erase) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
for(std::size_t next{}, last = minimum_bucket_count + 1u; next < last; ++next) {
@ -553,7 +553,7 @@ TEST(DenseSet, Erase) {
}
TEST(DenseSet, EraseWithMovableKeyValue) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::string> set;
set.emplace("0");
@ -570,7 +570,7 @@ TEST(DenseSet, EraseWithMovableKeyValue) {
}
TEST(DenseSet, EraseFromBucket) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
@ -721,7 +721,7 @@ TEST(DenseSet, LocalIterator) {
static_assert(std::is_same_v<iterator::pointer, const std::size_t *>);
static_assert(std::is_same_v<iterator::reference, const std::size_t &>);
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
set.emplace(3u);
set.emplace(3u + minimum_bucket_count);
@ -749,7 +749,7 @@ TEST(DenseSet, ConstLocalIterator) {
static_assert(std::is_same_v<iterator::pointer, const std::size_t *>);
static_assert(std::is_same_v<iterator::reference, const std::size_t &>);
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
set.emplace(3u);
set.emplace(3u + minimum_bucket_count);
@ -790,7 +790,7 @@ TEST(DenseSet, LocalIteratorConversion) {
}
TEST(DenseSet, Rehash) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, entt::identity> set;
set.emplace(32u);
@ -865,7 +865,7 @@ TEST(DenseSet, Rehash) {
}
TEST(DenseSet, Reserve) {
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<int> set;
ASSERT_EQ(set.bucket_count(), minimum_bucket_count);
@ -877,7 +877,7 @@ TEST(DenseSet, Reserve) {
set.reserve(minimum_bucket_count);
ASSERT_EQ(set.bucket_count(), 2 * minimum_bucket_count);
ASSERT_EQ(set.bucket_count(), entt::next_power_of_two(std::ceil(minimum_bucket_count / set.max_load_factor())));
ASSERT_EQ(set.bucket_count(), entt::next_power_of_two(static_cast<std::size_t>(std::ceil(minimum_bucket_count / set.max_load_factor()))));
}
TEST(DenseSet, ThrowingAllocator) {
@ -885,7 +885,7 @@ TEST(DenseSet, ThrowingAllocator) {
using packed_allocator = test::throwing_allocator<std::pair<std::size_t, std::size_t>>;
using packed_exception = typename packed_allocator::exception_type;
static constexpr std::size_t minimum_bucket_count = 8u;
constexpr std::size_t minimum_bucket_count = 8u;
entt::dense_set<std::size_t, std::hash<std::size_t>, std::equal_to<std::size_t>, allocator> set{};
packed_allocator::trigger_on_allocate = true;

View File

@ -2,6 +2,7 @@
#include <cstdint>
#include <cstring>
#include <iterator>
#include <memory>
#include <type_traits>
#include <unordered_map>
#include <utility>
@ -39,21 +40,9 @@ struct not_comparable {
bool operator==(const not_comparable &) const = delete;
};
struct not_copyable {
not_copyable()
: payload{} {}
not_copyable(const not_copyable &) = delete;
not_copyable(not_copyable &&) = default;
not_copyable &operator=(const not_copyable &) = delete;
not_copyable &operator=(not_copyable &&) = default;
double payload;
};
struct not_movable {
not_movable() = default;
not_movable(const not_movable &) = default;
not_movable(not_movable &&) = delete;
@ -1183,13 +1172,12 @@ TEST_F(Any, AnyCast) {
ASSERT_EQ(entt::any_cast<int &>(any), 42);
ASSERT_EQ(entt::any_cast<const int &>(cany), 42);
not_copyable instance{};
instance.payload = 42.;
auto instance = std::make_unique<double>(42.);
entt::any ref{entt::forward_as_any(instance)};
entt::any cref{entt::forward_as_any(std::as_const(instance).payload)};
entt::any cref{entt::forward_as_any(std::as_const(*instance))};
ASSERT_EQ(entt::any_cast<not_copyable>(std::move(ref)).payload, 42.);
ASSERT_EQ(entt::any_cast<double>(std::move(cref)), 42.);
ASSERT_EQ(*entt::any_cast<std::unique_ptr<double>>(std::move(ref)), 42.);
ASSERT_EQ(entt::any_cast<int>(entt::any{42}), 42);
}
@ -1197,16 +1185,15 @@ ENTT_DEBUG_TEST_F(AnyDeathTest, AnyCast) {
entt::any any{42};
const auto &cany = any;
ASSERT_DEATH(entt::any_cast<double &>(any), "");
ASSERT_DEATH(entt::any_cast<const double &>(cany), "");
ASSERT_DEATH([[maybe_unused]] auto &elem = entt::any_cast<double &>(any), "");
ASSERT_DEATH([[maybe_unused]] const auto &elem = entt::any_cast<const double &>(cany), "");
not_copyable instance{};
instance.payload = 42.;
auto instance = std::make_unique<double>(42.);
entt::any ref{entt::forward_as_any(instance)};
entt::any cref{entt::forward_as_any(std::as_const(instance).payload)};
entt::any cref{entt::forward_as_any(std::as_const(*instance))};
ASSERT_DEATH(entt::any_cast<not_copyable>(std::as_const(ref).as_ref()), "");
ASSERT_DEATH(entt::any_cast<double>(entt::any{42}), "");
ASSERT_DEATH([[maybe_unused]] auto elem = entt::any_cast<std::unique_ptr<double>>(std::as_const(ref).as_ref()), "");
ASSERT_DEATH([[maybe_unused]] auto elem = entt::any_cast<double>(entt::any{42}), "");
}
TEST_F(Any, MakeAny) {
@ -1263,8 +1250,8 @@ TEST_F(Any, ForwardAsAny) {
}
TEST_F(Any, NotCopyableType) {
const not_copyable value{};
entt::any any{std::in_place_type<not_copyable>};
const std::unique_ptr<int> value{};
entt::any any{std::in_place_type<std::unique_ptr<int>>};
entt::any other = entt::forward_as_any(value);
ASSERT_TRUE(any);
@ -1296,7 +1283,7 @@ TEST_F(Any, NotCopyableType) {
TEST_F(Any, NotCopyableValueType) {
std::vector<entt::any> vec{};
vec.emplace_back(std::in_place_type<not_copyable>);
vec.emplace_back(std::in_place_type<std::unique_ptr<int>>);
vec.shrink_to_fit();
ASSERT_EQ(vec.size(), 1u);
@ -1304,7 +1291,7 @@ TEST_F(Any, NotCopyableValueType) {
ASSERT_TRUE(vec[0u]);
// strong exception guarantee due to noexcept move ctor
vec.emplace_back(std::in_place_type<not_copyable>);
vec.emplace_back(std::in_place_type<std::unique_ptr<int>>);
ASSERT_EQ(vec.size(), 2u);
ASSERT_TRUE(vec[0u]);
@ -1411,7 +1398,7 @@ TEST_F(Any, SBOVsZeroedSBOSize) {
}
TEST_F(Any, SboAlignment) {
static constexpr auto alignment = alignof(over_aligned);
constexpr auto alignment = alignof(over_aligned);
entt::basic_any<alignment, alignment> sbo[2] = {over_aligned{}, over_aligned{}};
const auto *data = sbo[0].data();
@ -1427,7 +1414,7 @@ TEST_F(Any, SboAlignment) {
}
TEST_F(Any, NoSboAlignment) {
static constexpr auto alignment = alignof(over_aligned);
constexpr auto alignment = alignof(over_aligned);
entt::basic_any<alignment> nosbo[2] = {over_aligned{}, over_aligned{}};
const auto *data = nosbo[0].data();

View File

@ -6,19 +6,19 @@
#include <entt/core/hashed_string.hpp>
template<typename>
struct foobar_t;
struct expected;
template<>
struct foobar_t<std::uint32_t> {
struct expected<std::uint32_t> {
static constexpr auto value = 0xbf9cf968;
};
template<>
struct foobar_t<std::uint64_t> {
struct expected<std::uint64_t> {
static constexpr auto value = 0x85944171f73967e8;
};
inline constexpr auto foobar_v = foobar_t<entt::id_type>::value;
inline constexpr auto expected_v = expected<entt::id_type>::value;
TEST(BasicHashedString, DeductionGuide) {
static_assert(std::is_same_v<decltype(entt::basic_hashed_string{"foo"}), entt::hashed_string>);
@ -47,8 +47,8 @@ TEST(HashedString, Functionalities) {
entt::hashed_string hs{"foobar"};
ASSERT_EQ(static_cast<hash_type>(hs), foobar_v);
ASSERT_EQ(hs.value(), foobar_v);
ASSERT_EQ(static_cast<hash_type>(hs), expected_v);
ASSERT_EQ(hs.value(), expected_v);
ASSERT_EQ(foo_hs, "foo"_hs);
ASSERT_NE(bar_hs, "foo"_hs);
@ -78,13 +78,13 @@ TEST(HashedString, Correctness) {
const char *foobar = "foobar";
std::string_view view{"foobar__", 6};
ASSERT_EQ(entt::hashed_string{foobar}, foobar_v);
ASSERT_EQ((entt::hashed_string{view.data(), view.size()}), foobar_v);
ASSERT_EQ(entt::hashed_string{"foobar"}, foobar_v);
ASSERT_EQ(entt::hashed_string{foobar}, expected_v);
ASSERT_EQ((entt::hashed_string{view.data(), view.size()}), expected_v);
ASSERT_EQ(entt::hashed_string{"foobar"}, expected_v);
ASSERT_EQ(entt::hashed_string::value(foobar), foobar_v);
ASSERT_EQ(entt::hashed_string::value(view.data(), view.size()), foobar_v);
ASSERT_EQ(entt::hashed_string::value("foobar"), foobar_v);
ASSERT_EQ(entt::hashed_string::value(foobar), expected_v);
ASSERT_EQ(entt::hashed_string::value(view.data(), view.size()), expected_v);
ASSERT_EQ(entt::hashed_string::value("foobar"), expected_v);
ASSERT_EQ(entt::hashed_string{foobar}.size(), 6u);
ASSERT_EQ((entt::hashed_string{view.data(), view.size()}).size(), 6u);
@ -111,16 +111,16 @@ TEST(HashedString, Constexprness) {
constexpr std::string_view view{"foobar__", 6};
static_assert(entt::hashed_string{"quux"} == "quux"_hs);
static_assert(entt::hashed_string{"foobar"} == foobar_v);
static_assert(entt::hashed_string{"foobar"} == expected_v);
static_assert(entt::hashed_string::value("quux") == "quux"_hs);
static_assert(entt::hashed_string::value("foobar") == foobar_v);
static_assert(entt::hashed_string::value("foobar") == expected_v);
static_assert(entt::hashed_string{"quux", 4} == "quux"_hs);
static_assert(entt::hashed_string{view.data(), view.size()} == foobar_v);
static_assert(entt::hashed_string{view.data(), view.size()} == expected_v);
static_assert(entt::hashed_string::value("quux", 4) == "quux"_hs);
static_assert(entt::hashed_string::value(view.data(), view.size()) == foobar_v);
static_assert(entt::hashed_string::value(view.data(), view.size()) == expected_v);
static_assert(entt::hashed_string{"bar"} < "foo"_hs);
static_assert(entt::hashed_string{"bar"} <= "bar"_hs);
@ -151,8 +151,8 @@ TEST(HashedWString, Functionalities) {
entt::hashed_wstring hws{L"foobar"};
ASSERT_EQ(static_cast<hash_type>(hws), foobar_v);
ASSERT_EQ(hws.value(), foobar_v);
ASSERT_EQ(static_cast<hash_type>(hws), expected_v);
ASSERT_EQ(hws.value(), expected_v);
ASSERT_EQ(foo_hws, L"foo"_hws);
ASSERT_NE(bar_hws, L"foo"_hws);
@ -172,13 +172,13 @@ TEST(HashedWString, Correctness) {
const wchar_t *foobar = L"foobar";
std::wstring_view view{L"foobar__", 6};
ASSERT_EQ(entt::hashed_wstring{foobar}, foobar_v);
ASSERT_EQ((entt::hashed_wstring{view.data(), view.size()}), foobar_v);
ASSERT_EQ(entt::hashed_wstring{L"foobar"}, foobar_v);
ASSERT_EQ(entt::hashed_wstring{foobar}, expected_v);
ASSERT_EQ((entt::hashed_wstring{view.data(), view.size()}), expected_v);
ASSERT_EQ(entt::hashed_wstring{L"foobar"}, expected_v);
ASSERT_EQ(entt::hashed_wstring::value(foobar), foobar_v);
ASSERT_EQ(entt::hashed_wstring::value(view.data(), view.size()), foobar_v);
ASSERT_EQ(entt::hashed_wstring::value(L"foobar"), foobar_v);
ASSERT_EQ(entt::hashed_wstring::value(foobar), expected_v);
ASSERT_EQ(entt::hashed_wstring::value(view.data(), view.size()), expected_v);
ASSERT_EQ(entt::hashed_wstring::value(L"foobar"), expected_v);
ASSERT_EQ(entt::hashed_wstring{foobar}.size(), 6u);
ASSERT_EQ((entt::hashed_wstring{view.data(), view.size()}).size(), 6u);
@ -205,16 +205,16 @@ TEST(HashedWString, Constexprness) {
constexpr std::wstring_view view{L"foobar__", 6};
static_assert(entt::hashed_wstring{L"quux"} == L"quux"_hws);
static_assert(entt::hashed_wstring{L"foobar"} == foobar_v);
static_assert(entt::hashed_wstring{L"foobar"} == expected_v);
static_assert(entt::hashed_wstring::value(L"quux") == L"quux"_hws);
static_assert(entt::hashed_wstring::value(L"foobar") == foobar_v);
static_assert(entt::hashed_wstring::value(L"foobar") == expected_v);
static_assert(entt::hashed_wstring{L"quux", 4} == L"quux"_hws);
static_assert(entt::hashed_wstring{view.data(), view.size()} == foobar_v);
static_assert(entt::hashed_wstring{view.data(), view.size()} == expected_v);
static_assert(entt::hashed_wstring::value(L"quux", 4) == L"quux"_hws);
static_assert(entt::hashed_wstring::value(view.data(), view.size()) == foobar_v);
static_assert(entt::hashed_wstring::value(view.data(), view.size()) == expected_v);
static_assert(entt::hashed_wstring{L"bar"} < L"foo"_hws);
static_assert(entt::hashed_wstring{L"bar"} <= L"bar"_hws);

View File

@ -24,6 +24,12 @@ TEST(ToAddress, Functionalities) {
TEST(PoccaPocmaAndPocs, Functionalities) {
test::basic_test_allocator<int> lhs, rhs;
test::basic_test_allocator<int, std::false_type> no_pocs;
// code coverage purposes
ASSERT_FALSE(lhs == rhs);
ASSERT_NO_FATAL_FAILURE(entt::propagate_on_container_swap(no_pocs, no_pocs));
// honestly, I don't even know how one is supposed to test such a thing :)
entt::propagate_on_container_copy_assignment(lhs, rhs);
entt::propagate_on_container_move_assignment(lhs, rhs);
@ -31,8 +37,8 @@ TEST(PoccaPocmaAndPocs, Functionalities) {
}
ENTT_DEBUG_TEST(PoccaPocmaAndPocsDeathTest, Functionalities) {
using pocs = std::false_type;
test::basic_test_allocator<int, pocs> lhs, rhs;
test::basic_test_allocator<int, std::false_type> lhs, rhs;
ASSERT_DEATH(entt::propagate_on_container_swap(lhs, rhs), "");
}
@ -60,8 +66,8 @@ TEST(NextPowerOfTwo, Functionalities) {
ASSERT_EQ(entt::next_power_of_two(17u), 32u);
ASSERT_EQ(entt::next_power_of_two(32u), 32u);
ASSERT_EQ(entt::next_power_of_two(33u), 64u);
ASSERT_EQ(entt::next_power_of_two(std::pow(2, 16)), std::pow(2, 16));
ASSERT_EQ(entt::next_power_of_two(std::pow(2, 16) + 1u), std::pow(2, 17));
ASSERT_EQ(entt::next_power_of_two(static_cast<std::size_t>(std::pow(2, 16))), static_cast<std::size_t>(std::pow(2, 16)));
ASSERT_EQ(entt::next_power_of_two(static_cast<std::size_t>(std::pow(2, 16) + 1u)), static_cast<std::size_t>(std::pow(2, 17)));
}
ENTT_DEBUG_TEST(NextPowerOfTwoDeathTest, Functionalities) {

View File

@ -34,5 +34,5 @@ TEST(Tuple, UnwrapTuple) {
TEST(Tuple, ForwardApply) {
ASSERT_EQ(entt::forward_apply{[](auto &&...args) { return sizeof...(args); }}(std::make_tuple()), 0u);
ASSERT_EQ(entt::forward_apply{[](int i) { return i; }}(std::make_tuple(42)), 42);
ASSERT_EQ(entt::forward_apply{[](auto... args) { return (args + ...); }}(std::make_tuple('a', 1u)), 'b');
ASSERT_EQ(entt::forward_apply{[](auto... args) { return (args + ...); }}(std::make_tuple('a', 1)), 'b');
}

View File

@ -89,7 +89,7 @@ TEST(TypeList, Functionalities) {
static_assert(std::is_same_v<entt::type_list_cat_t<type, other, type, other>, entt::type_list<int, char, double, int, char, double>>);
static_assert(std::is_same_v<entt::type_list_cat_t<type, other>, entt::type_list<int, char, double>>);
static_assert(std::is_same_v<entt::type_list_cat_t<type, type>, entt::type_list<int, char, int, char>>);
static_assert(std::is_same_v<entt::type_list_unique_t<entt::type_list_cat_t<type, type>>, entt::type_list<int, char>>);
static_assert(std::is_same_v<entt::type_list_unique_t<entt::type_list_cat_t<type, type>>, type>);
static_assert(entt::type_list_contains_v<type, int>);
static_assert(entt::type_list_contains_v<type, char>);
@ -112,6 +112,14 @@ TEST(TypeList, Functionalities) {
static_assert(std::is_same_v<entt::type_list_transform_t<entt::type_list<int, char>, entt::type_identity>, entt::type_list<int, char>>);
static_assert(std::is_same_v<entt::type_list_transform_t<entt::type_list<int, char>, std::add_const>, entt::type_list<const int, const char>>);
static_assert(std::is_same_v<entt::type_list_transform_t<entt::type_list<int, char>, multi_argument_operation>, entt::type_list<void, void>>);
static_assert(std::tuple_size_v<entt::type_list<>> == 0u);
static_assert(std::tuple_size_v<entt::type_list<int>> == 1u);
static_assert(std::tuple_size_v<entt::type_list<int, float>> == 2u);
static_assert(std::is_same_v<int, std::tuple_element_t<0, entt::type_list<int>>>);
static_assert(std::is_same_v<int, std::tuple_element_t<0, entt::type_list<int, float>>>);
static_assert(std::is_same_v<float, std::tuple_element_t<1, entt::type_list<int, float>>>);
}
TEST(ValueList, Functionalities) {
@ -125,10 +133,33 @@ TEST(ValueList, Functionalities) {
static_assert(std::is_same_v<entt::value_list_cat_t<value, other, value, other>, entt::value_list<0, 2, 1, 0, 2, 1>>);
static_assert(std::is_same_v<entt::value_list_cat_t<value, other>, entt::value_list<0, 2, 1>>);
static_assert(std::is_same_v<entt::value_list_cat_t<value, value>, entt::value_list<0, 2, 0, 2>>);
static_assert(std::is_same_v<entt::value_list_unique_t<entt::value_list_cat_t<value, value>>, value>);
static_assert(entt::value_list_contains_v<value, 0>);
static_assert(entt::value_list_contains_v<value, 2>);
static_assert(!entt::value_list_contains_v<value, 1>);
static_assert(entt::value_list_element_v<0u, value> == 0);
static_assert(entt::value_list_element_v<1u, value> == 2);
static_assert(entt::value_list_element_v<0u, other> == 1);
static_assert(entt::value_list_index_v<0, value> == 0u);
static_assert(entt::value_list_index_v<2, value> == 1u);
static_assert(entt::value_list_index_v<1, other> == 0u);
static_assert(std::is_same_v<entt::value_list_diff_t<entt::value_list<0, 1, 2>, entt::value_list<3, 4>>, entt::value_list<0, 1, 2>>);
static_assert(std::is_same_v<entt::value_list_diff_t<entt::value_list<0, 1, 2>, entt::value_list<0, 1, 2>>, entt::value_list<>>);
static_assert(std::is_same_v<entt::value_list_diff_t<entt::value_list<0, 1, 2>, entt::value_list<0, 1>>, entt::value_list<2>>);
static_assert(std::is_same_v<entt::value_list_diff_t<entt::value_list<0, 1, 2>, entt::value_list<1, 2>>, entt::value_list<0>>);
static_assert(std::is_same_v<entt::value_list_diff_t<entt::value_list<0, 1, 2>, entt::value_list<1>>, entt::value_list<0, 2>>);
static_assert(std::tuple_size_v<entt::value_list<>> == 0u);
static_assert(std::tuple_size_v<entt::value_list<42>> == 1u);
static_assert(std::tuple_size_v<entt::value_list<42, 'a'>> == 2u);
static_assert(std::is_same_v<int, std::tuple_element_t<0, entt::value_list<42>>>);
static_assert(std::is_same_v<int, std::tuple_element_t<0, entt::value_list<42, 'a'>>>);
static_assert(std::is_same_v<char, std::tuple_element_t<1, entt::value_list<42, 'a'>>>);
}
TEST(IsApplicable, Functionalities) {
@ -183,6 +214,7 @@ TEST(IsEqualityComparable, Functionalities) {
static_assert(entt::is_equality_comparable_v<std::vector<not_comparable>::iterator>);
static_assert(entt::is_equality_comparable_v<nlohmann_json_like>);
static_assert(!entt::is_equality_comparable_v<int[3u]>);
static_assert(!entt::is_equality_comparable_v<not_comparable>);
static_assert(!entt::is_equality_comparable_v<const not_comparable>);
static_assert(!entt::is_equality_comparable_v<std::vector<not_comparable>>);

View File

@ -30,50 +30,43 @@ struct entt::component_traits<traits_based> {
};
TEST(Component, VoidType) {
using traits = entt::component_traits<void>;
using traits_type = entt::component_traits<void>;
static_assert(traits::in_place_delete);
static_assert(entt::ignore_as_empty_v<typename traits::type>);
// we don't really care about this thanks to ignore_as_empty_v
static_assert(traits::page_size != 0u);
static_assert(!traits_type::in_place_delete);
static_assert(traits_type::page_size == 0u);
}
TEST(Component, Empty) {
using traits = entt::component_traits<empty>;
using traits_type = entt::component_traits<empty>;
static_assert(!traits::in_place_delete);
static_assert(entt::ignore_as_empty_v<typename traits::type>);
static_assert(traits::page_size == 0u);
static_assert(!traits_type::in_place_delete);
static_assert(traits_type::page_size == 0u);
}
TEST(Component, NonEmpty) {
using traits = entt::component_traits<non_empty>;
using traits_type = entt::component_traits<non_empty>;
static_assert(!traits::in_place_delete);
static_assert(!entt::ignore_as_empty_v<typename traits::type>);
static_assert(traits::page_size == ENTT_PACKED_PAGE);
static_assert(!traits_type::in_place_delete);
static_assert(traits_type::page_size == ENTT_PACKED_PAGE);
}
TEST(Component, NonMovable) {
using traits = entt::component_traits<non_movable>;
using traits_type = entt::component_traits<non_movable>;
static_assert(traits::in_place_delete);
static_assert(!entt::ignore_as_empty_v<typename traits::type>);
static_assert(traits::page_size == ENTT_PACKED_PAGE);
static_assert(traits_type::in_place_delete);
static_assert(traits_type::page_size == ENTT_PACKED_PAGE);
}
TEST(Component, SelfContained) {
using traits = entt::component_traits<self_contained>;
using traits_type = entt::component_traits<self_contained>;
static_assert(traits::in_place_delete);
static_assert(!entt::ignore_as_empty_v<typename traits::type>);
static_assert(traits::page_size == 4u);
static_assert(traits_type::in_place_delete);
static_assert(traits_type::page_size == 4u);
}
TEST(Component, TraitsBased) {
using traits = entt::component_traits<traits_based>;
using traits_type = entt::component_traits<traits_based>;
static_assert(!traits::in_place_delete);
static_assert(!entt::ignore_as_empty_v<typename traits::type>);
static_assert(traits::page_size == 8u);
static_assert(!traits_type::in_place_delete);
static_assert(traits_type::page_size == 8u);
}

View File

@ -29,6 +29,12 @@ TEST(Entity, Traits) {
ASSERT_EQ(traits_type::combine(entt::tombstone, entt::null), tombstone);
ASSERT_EQ(traits_type::combine(entt::null, entt::tombstone), null);
ASSERT_EQ(traits_type::next(entity), traits_type::construct(entt::to_integral(entity), entt::to_version(entity) + 1u));
ASSERT_EQ(traits_type::next(other), traits_type::construct(entt::to_integral(other), entt::to_version(other) + 1u));
ASSERT_EQ(traits_type::next(entt::tombstone), traits_type::construct(entt::null, {}));
ASSERT_EQ(traits_type::next(entt::null), traits_type::construct(entt::null, {}));
}
TEST(Entity, Null) {

View File

@ -8,6 +8,7 @@
#include <gtest/gtest.h>
#include <entt/entity/group.hpp>
#include <entt/entity/registry.hpp>
#include "../common/config.h"
struct empty_type {};
@ -56,7 +57,7 @@ TEST(NonOwningGroup, Functionalities) {
for(auto entity: group) {
ASSERT_EQ(std::get<0>(cgroup.get<const int, const char>(entity)), 42);
ASSERT_EQ(std::get<1>(group.get<int, char>(entity)), '2');
ASSERT_EQ(cgroup.get<const char>(entity), '2');
ASSERT_EQ(cgroup.get<1>(entity), '2');
}
ASSERT_EQ(group.handle().data()[0u], e1);
@ -93,6 +94,7 @@ TEST(NonOwningGroup, Handle) {
ASSERT_TRUE(handle.empty());
ASSERT_FALSE(handle.contains(entity));
ASSERT_EQ(&handle, &group.handle());
ASSERT_NE(&handle, group.storage<int>());
registry.emplace<int>(entity);
registry.emplace<char>(entity);
@ -187,10 +189,10 @@ TEST(NonOwningGroup, Each) {
auto cgroup = std::as_const(registry).group_if_exists(entt::get<const int, const char>);
registry.emplace<int>(entity[0u], 0);
registry.emplace<char>(entity[0u], 0);
registry.emplace<char>(entity[0u], static_cast<char>(0));
registry.emplace<int>(entity[1u], 1);
registry.emplace<char>(entity[1u], 1);
registry.emplace<char>(entity[1u], static_cast<char>(1));
auto iterable = group.each();
auto citerable = cgroup.each();
@ -201,16 +203,18 @@ TEST(NonOwningGroup, Each) {
auto it = iterable.begin();
ASSERT_EQ(it.base(), group.begin());
ASSERT_EQ((it++, ++it), iterable.end());
ASSERT_EQ(it.base(), group.end());
group.each([expected = 1u](auto entt, int &ivalue, char &cvalue) mutable {
ASSERT_EQ(entt::to_integral(entt), expected);
group.each([expected = 1](auto entt, int &ivalue, char &cvalue) mutable {
ASSERT_EQ(static_cast<int>(entt::to_integral(entt)), expected);
ASSERT_EQ(ivalue, expected);
ASSERT_EQ(cvalue, expected);
--expected;
});
cgroup.each([expected = 1u](const int &ivalue, const char &cvalue) mutable {
cgroup.each([expected = 1](const int &ivalue, const char &cvalue) mutable {
ASSERT_EQ(ivalue, expected);
ASSERT_EQ(cvalue, expected);
--expected;
@ -224,8 +228,8 @@ TEST(NonOwningGroup, Each) {
// do not use iterable, make sure an iterable group works when created from a temporary
for(auto [entt, ivalue, cvalue]: registry.group(entt::get<int, char>).each()) {
ASSERT_EQ(entt::to_integral(entt), ivalue);
ASSERT_EQ(entt::to_integral(entt), cvalue);
ASSERT_EQ(static_cast<int>(entt::to_integral(entt)), ivalue);
ASSERT_EQ(static_cast<char>(entt::to_integral(entt)), cvalue);
}
}
@ -273,9 +277,9 @@ TEST(NonOwningGroup, Sort) {
ASSERT_EQ(group.handle().data()[1u], e1);
ASSERT_EQ(group.handle().data()[2u], e2);
ASSERT_EQ((group.get<const int, unsigned int>(e0)), (std::make_tuple(0, 0u)));
ASSERT_EQ((group.get<const int, unsigned int>(e1)), (std::make_tuple(1, 1u)));
ASSERT_EQ((group.get<const int, unsigned int>(e2)), (std::make_tuple(2, 2u)));
ASSERT_EQ((group.get<0, 1>(e0)), (std::make_tuple(0, 0u)));
ASSERT_EQ((group.get<0, 1>(e1)), (std::make_tuple(1, 1u)));
ASSERT_EQ((group.get<0, 1>(e2)), (std::make_tuple(2, 2u)));
ASSERT_FALSE(group.contains(e3));
@ -323,10 +327,10 @@ TEST(NonOwningGroup, SortAsAPool) {
}
registry.sort<unsigned int>(std::less<unsigned int>{});
group.sort<unsigned int>();
group.sort_as(*group.storage<unsigned int>());
ASSERT_EQ((group.get<const int, unsigned int>(e0)), (std::make_tuple(0, 0u)));
ASSERT_EQ((group.get<const int, unsigned int>(e1)), (std::make_tuple(1, 1u)));
ASSERT_EQ((group.get<0, 1>(e1)), (std::make_tuple(1, 1u)));
ASSERT_EQ((group.get<const int, unsigned int>(e2)), (std::make_tuple(2, 2u)));
ASSERT_FALSE(group.contains(e3));
@ -384,9 +388,18 @@ TEST(NonOwningGroup, ConstNonConstAndAllInBetween) {
ASSERT_EQ(group.size(), 1u);
static_assert(std::is_same_v<decltype(group.get<0>({})), int &>);
static_assert(std::is_same_v<decltype(group.get<int>({})), int &>);
static_assert(std::is_same_v<decltype(group.get<1>({})), void>);
static_assert(std::is_same_v<decltype(group.get<empty_type>({})), void>);
static_assert(std::is_same_v<decltype(group.get<2>({})), const char &>);
static_assert(std::is_same_v<decltype(group.get<const char>({})), const char &>);
static_assert(std::is_same_v<decltype(group.get<int, const char>({})), std::tuple<int &, const char &>>);
static_assert(std::is_same_v<decltype(group.get<int, empty_type, const char>({})), std::tuple<int &, const char &>>);
static_assert(std::is_same_v<decltype(group.get<0, 1, 2>({})), std::tuple<int &, const char &>>);
static_assert(std::is_same_v<decltype(group.get({})), std::tuple<int &, const char &>>);
static_assert(std::is_same_v<decltype(std::as_const(registry).group_if_exists(entt::get<int, char>)), decltype(std::as_const(registry).group_if_exists(entt::get<const int, const char>))>);
@ -475,7 +488,7 @@ TEST(NonOwningGroup, ExcludedComponents) {
if(entity == e0) {
ASSERT_EQ(group.get<int>(e0), 0);
} else if(entity == e2) {
ASSERT_EQ(group.get<int>(e2), 2);
ASSERT_EQ(group.get<0>(e2), 2);
}
}
@ -493,7 +506,7 @@ TEST(NonOwningGroup, ExcludedComponents) {
if(entity == e1) {
ASSERT_EQ(group.get<int>(e1), 1);
} else if(entity == e3) {
ASSERT_EQ(group.get<int>(e3), 3);
ASSERT_EQ(group.get<0>(e3), 3);
}
}
}
@ -663,28 +676,100 @@ TEST(NonOwningGroup, IterableGroupAlgorithmCompatibility) {
TEST(NonOwningGroup, Storage) {
entt::registry registry;
const auto entity = registry.create();
const auto group = registry.group(entt::get<int, const char>);
auto group = registry.group(entt::get<int, const char>, entt::exclude<double, const float>);
static_assert(std::is_same_v<decltype(group.storage<0u>()), entt::storage_type_t<int> &>);
static_assert(std::is_same_v<decltype(group.storage<int>()), entt::storage_type_t<int> &>);
static_assert(std::is_same_v<decltype(group.storage<1u>()), const entt::storage_type_t<char> &>);
static_assert(std::is_same_v<decltype(group.storage<const char>()), const entt::storage_type_t<char> &>);
static_assert(std::is_same_v<decltype(group.storage<0u>()), entt::storage_type_t<int> *>);
static_assert(std::is_same_v<decltype(group.storage<int>()), entt::storage_type_t<int> *>);
static_assert(std::is_same_v<decltype(group.storage<const int>()), entt::storage_type_t<int> *>);
static_assert(std::is_same_v<decltype(group.storage<1u>()), const entt::storage_type_t<char> *>);
static_assert(std::is_same_v<decltype(group.storage<char>()), const entt::storage_type_t<char> *>);
static_assert(std::is_same_v<decltype(group.storage<const char>()), const entt::storage_type_t<char> *>);
static_assert(std::is_same_v<decltype(group.storage<2u>()), entt::storage_type_t<double> *>);
static_assert(std::is_same_v<decltype(group.storage<double>()), entt::storage_type_t<double> *>);
static_assert(std::is_same_v<decltype(group.storage<const double>()), entt::storage_type_t<double> *>);
static_assert(std::is_same_v<decltype(group.storage<3u>()), const entt::storage_type_t<float> *>);
static_assert(std::is_same_v<decltype(group.storage<float>()), const entt::storage_type_t<float> *>);
static_assert(std::is_same_v<decltype(group.storage<const float>()), const entt::storage_type_t<float> *>);
ASSERT_TRUE(group);
ASSERT_NE(group.storage<int>(), nullptr);
ASSERT_NE(group.storage<1u>(), nullptr);
ASSERT_NE(group.storage<double>(), nullptr);
ASSERT_NE(group.storage<3u>(), nullptr);
ASSERT_EQ(group.size(), 0u);
group.storage<int>().emplace(entity);
group.storage<int>()->emplace(entity);
group.storage<double>()->emplace(entity);
registry.emplace<char>(entity);
registry.emplace<float>(entity);
ASSERT_EQ(group.size(), 0u);
ASSERT_EQ(group.begin(), group.end());
ASSERT_TRUE(group.storage<int>()->contains(entity));
ASSERT_TRUE(group.storage<const char>()->contains(entity));
ASSERT_TRUE(group.storage<double>()->contains(entity));
ASSERT_TRUE(group.storage<const float>()->contains(entity));
ASSERT_TRUE((registry.all_of<int, char, double, float>(entity)));
group.storage<double>()->erase(entity);
registry.erase<float>(entity);
ASSERT_EQ(group.size(), 1u);
ASSERT_TRUE(group.storage<int>().contains(entity));
ASSERT_TRUE(group.storage<const char>().contains(entity));
ASSERT_NE(group.begin(), group.end());
ASSERT_TRUE(group.storage<const int>()->contains(entity));
ASSERT_TRUE(group.storage<char>()->contains(entity));
ASSERT_FALSE(group.storage<const double>()->contains(entity));
ASSERT_FALSE(group.storage<float>()->contains(entity));
ASSERT_TRUE((registry.all_of<int, char>(entity)));
ASSERT_FALSE((registry.any_of<double, float>(entity)));
group.storage<0u>().erase(entity);
group.storage<0u>()->erase(entity);
ASSERT_EQ(group.size(), 0u);
ASSERT_TRUE(group.storage<1u>().contains(entity));
ASSERT_FALSE((registry.all_of<int, char>(entity)));
ASSERT_EQ(group.begin(), group.end());
ASSERT_FALSE(group.storage<0u>()->contains(entity));
ASSERT_TRUE(group.storage<1u>()->contains(entity));
ASSERT_FALSE(group.storage<2u>()->contains(entity));
ASSERT_FALSE(group.storage<3u>()->contains(entity));
ASSERT_TRUE((registry.all_of<char>(entity)));
ASSERT_FALSE((registry.any_of<int, double, float>(entity)));
group = {};
ASSERT_FALSE(group);
ASSERT_EQ(group.storage<0u>(), nullptr);
ASSERT_EQ(group.storage<const char>(), nullptr);
ASSERT_EQ(group.storage<2u>(), nullptr);
ASSERT_EQ(group.storage<const float>(), nullptr);
}
TEST(NonOwningGroup, Overlapping) {
entt::registry registry;
auto group = registry.group(entt::get<char>, entt::exclude<double>);
auto other = registry.group<int>(entt::get<char>, entt::exclude<double>);
ASSERT_TRUE(group.empty());
ASSERT_TRUE(other.empty());
const auto entity = registry.create();
registry.emplace<char>(entity, '1');
ASSERT_FALSE(group.empty());
ASSERT_TRUE(other.empty());
registry.emplace<int>(entity, 42);
ASSERT_FALSE(group.empty());
ASSERT_FALSE(other.empty());
registry.emplace<double>(entity, 3.);
ASSERT_TRUE(group.empty());
ASSERT_TRUE(other.empty());
}
TEST(OwningGroup, Functionalities) {
@ -721,16 +806,17 @@ TEST(OwningGroup, Functionalities) {
ASSERT_EQ(group.size(), 1u);
ASSERT_EQ(cgroup.storage<const int>().raw()[0u][0u], 42);
ASSERT_EQ(group.storage<int>().raw()[0u][0u], 42);
ASSERT_EQ(cgroup.storage<const int>()->raw()[0u][0u], 42);
ASSERT_EQ(group.storage<int>()->raw()[0u][0u], 42);
for(auto entity: group) {
ASSERT_EQ(std::get<0>(cgroup.get<const int, const char>(entity)), 42);
ASSERT_EQ(std::get<1>(group.get<int, char>(entity)), '2');
ASSERT_EQ(cgroup.get<const char>(entity), '2');
ASSERT_EQ(cgroup.get<1>(entity), '2');
}
ASSERT_EQ(group.storage<int>().raw()[0u][0u], 42);
ASSERT_EQ(group.handle().data()[0u], e1);
ASSERT_EQ(group.storage<int>()->raw()[0u][0u], 42);
registry.erase<char>(e0);
registry.erase<char>(e1);
@ -748,6 +834,26 @@ TEST(OwningGroup, Functionalities) {
ASSERT_FALSE(invalid);
}
TEST(OwningGroup, Handle) {
entt::registry registry;
const auto entity = registry.create();
auto group = registry.group<int>(entt::get<char>);
auto &&handle = group.handle();
ASSERT_TRUE(handle.empty());
ASSERT_FALSE(handle.contains(entity));
ASSERT_EQ(&handle, &group.handle());
ASSERT_EQ(&handle, group.storage<int>());
registry.emplace<int>(entity);
registry.emplace<char>(entity);
ASSERT_FALSE(handle.empty());
ASSERT_TRUE(handle.contains(entity));
ASSERT_EQ(&handle, &group.handle());
}
TEST(OwningGroup, Invalid) {
entt::registry registry{};
auto group = std::as_const(registry).group_if_exists<const int>(entt::get<const empty_type>);
@ -831,10 +937,10 @@ TEST(OwningGroup, Each) {
auto cgroup = std::as_const(registry).group_if_exists<const int>(entt::get<const char>);
registry.emplace<int>(entity[0u], 0);
registry.emplace<char>(entity[0u], 0);
registry.emplace<char>(entity[0u], static_cast<char>(0));
registry.emplace<int>(entity[1u], 1);
registry.emplace<char>(entity[1u], 1);
registry.emplace<char>(entity[1u], static_cast<char>(1));
auto iterable = group.each();
auto citerable = cgroup.each();
@ -845,16 +951,18 @@ TEST(OwningGroup, Each) {
auto it = iterable.begin();
ASSERT_EQ(it.base(), group.begin());
ASSERT_EQ((it++, ++it), iterable.end());
ASSERT_EQ(it.base(), group.end());
group.each([expected = 1u](auto entt, int &ivalue, char &cvalue) mutable {
ASSERT_EQ(entt::to_integral(entt), expected);
group.each([expected = 1](auto entt, int &ivalue, char &cvalue) mutable {
ASSERT_EQ(static_cast<int>(entt::to_integral(entt)), expected);
ASSERT_EQ(ivalue, expected);
ASSERT_EQ(cvalue, expected);
--expected;
});
cgroup.each([expected = 1u](const int &ivalue, const char &cvalue) mutable {
cgroup.each([expected = 1](const int &ivalue, const char &cvalue) mutable {
ASSERT_EQ(ivalue, expected);
ASSERT_EQ(cvalue, expected);
--expected;
@ -868,8 +976,8 @@ TEST(OwningGroup, Each) {
// do not use iterable, make sure an iterable group works when created from a temporary
for(auto [entt, ivalue, cvalue]: registry.group<int>(entt::get<char>).each()) {
ASSERT_EQ(entt::to_integral(entt), ivalue);
ASSERT_EQ(entt::to_integral(entt), cvalue);
ASSERT_EQ(static_cast<int>(entt::to_integral(entt)), ivalue);
ASSERT_EQ(static_cast<char>(entt::to_integral(entt)), cvalue);
}
}
@ -893,27 +1001,27 @@ TEST(OwningGroup, SortOrdered) {
registry.emplace<boxed_int>(entities[4], 2);
group.sort([&group](const entt::entity lhs, const entt::entity rhs) {
return group.get<boxed_int>(lhs).value < group.get<boxed_int>(rhs).value;
return group.get<boxed_int>(lhs).value < group.get<0>(rhs).value;
});
ASSERT_EQ(group.storage<boxed_int>().data()[0u], entities[0]);
ASSERT_EQ(group.storage<boxed_int>().data()[1u], entities[1]);
ASSERT_EQ(group.storage<boxed_int>().data()[2u], entities[2]);
ASSERT_EQ(group.storage<boxed_int>().data()[3u], entities[3]);
ASSERT_EQ(group.storage<boxed_int>().data()[4u], entities[4]);
ASSERT_EQ(group.handle().data()[0u], entities[0]);
ASSERT_EQ(group.handle().data()[1u], entities[1]);
ASSERT_EQ(group.handle().data()[2u], entities[2]);
ASSERT_EQ(group.handle().data()[3u], entities[3]);
ASSERT_EQ(group.handle().data()[4u], entities[4]);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][0u].value, 12);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][1u].value, 9);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][2u].value, 6);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][3u].value, 1);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][4u].value, 2);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][0u].value, 12);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][1u].value, 9);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][2u].value, 6);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][3u].value, 1);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][4u].value, 2);
ASSERT_EQ(group.storage<char>().raw()[0u][0u], 'a');
ASSERT_EQ(group.storage<char>().raw()[0u][1u], 'b');
ASSERT_EQ(group.storage<char>().raw()[0u][2u], 'c');
ASSERT_EQ(group.storage<char>()->raw()[0u][0u], 'a');
ASSERT_EQ(group.storage<char>()->raw()[0u][1u], 'b');
ASSERT_EQ(group.storage<char>()->raw()[0u][2u], 'c');
ASSERT_EQ((group.get<boxed_int, char>(entities[0])), (std::make_tuple(boxed_int{12}, 'a')));
ASSERT_EQ((group.get<boxed_int, char>(entities[1])), (std::make_tuple(boxed_int{9}, 'b')));
ASSERT_EQ((group.get<0, 1>(entities[1])), (std::make_tuple(boxed_int{9}, 'b')));
ASSERT_EQ((group.get<boxed_int, char>(entities[2])), (std::make_tuple(boxed_int{6}, 'c')));
ASSERT_FALSE(group.contains(entities[3]));
@ -943,24 +1051,24 @@ TEST(OwningGroup, SortReverse) {
return lhs.value < rhs.value;
});
ASSERT_EQ(group.storage<boxed_int>().data()[0u], entities[2]);
ASSERT_EQ(group.storage<boxed_int>().data()[1u], entities[1]);
ASSERT_EQ(group.storage<boxed_int>().data()[2u], entities[0]);
ASSERT_EQ(group.storage<boxed_int>().data()[3u], entities[3]);
ASSERT_EQ(group.storage<boxed_int>().data()[4u], entities[4]);
ASSERT_EQ(group.handle().data()[0u], entities[2]);
ASSERT_EQ(group.handle().data()[1u], entities[1]);
ASSERT_EQ(group.handle().data()[2u], entities[0]);
ASSERT_EQ(group.handle().data()[3u], entities[3]);
ASSERT_EQ(group.handle().data()[4u], entities[4]);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][0u].value, 12);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][1u].value, 9);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][2u].value, 6);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][3u].value, 1);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][4u].value, 2);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][0u].value, 12);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][1u].value, 9);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][2u].value, 6);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][3u].value, 1);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][4u].value, 2);
ASSERT_EQ(group.storage<char>().raw()[0u][0u], 'c');
ASSERT_EQ(group.storage<char>().raw()[0u][1u], 'b');
ASSERT_EQ(group.storage<char>().raw()[0u][2u], 'a');
ASSERT_EQ(group.storage<char>()->raw()[0u][0u], 'c');
ASSERT_EQ(group.storage<char>()->raw()[0u][1u], 'b');
ASSERT_EQ(group.storage<char>()->raw()[0u][2u], 'a');
ASSERT_EQ((group.get<boxed_int, char>(entities[0])), (std::make_tuple(boxed_int{6}, 'a')));
ASSERT_EQ((group.get<boxed_int, char>(entities[1])), (std::make_tuple(boxed_int{9}, 'b')));
ASSERT_EQ((group.get<0, 1>(entities[1])), (std::make_tuple(boxed_int{9}, 'b')));
ASSERT_EQ((group.get<boxed_int, char>(entities[2])), (std::make_tuple(boxed_int{12}, 'c')));
ASSERT_FALSE(group.contains(entities[3]));
@ -998,27 +1106,27 @@ TEST(OwningGroup, SortUnordered) {
return std::get<1>(lhs) < std::get<1>(rhs);
});
ASSERT_EQ(group.storage<boxed_int>().data()[0u], entities[4]);
ASSERT_EQ(group.storage<boxed_int>().data()[1u], entities[3]);
ASSERT_EQ(group.storage<boxed_int>().data()[2u], entities[0]);
ASSERT_EQ(group.storage<boxed_int>().data()[3u], entities[1]);
ASSERT_EQ(group.storage<boxed_int>().data()[4u], entities[2]);
ASSERT_EQ(group.storage<boxed_int>().data()[5u], entities[5]);
ASSERT_EQ(group.storage<boxed_int>().data()[6u], entities[6]);
ASSERT_EQ(group.handle().data()[0u], entities[4]);
ASSERT_EQ(group.handle().data()[1u], entities[3]);
ASSERT_EQ(group.handle().data()[2u], entities[0]);
ASSERT_EQ(group.handle().data()[3u], entities[1]);
ASSERT_EQ(group.handle().data()[4u], entities[2]);
ASSERT_EQ(group.handle().data()[5u], entities[5]);
ASSERT_EQ(group.handle().data()[6u], entities[6]);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][0u].value, 12);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][1u].value, 9);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][2u].value, 6);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][3u].value, 3);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][4u].value, 1);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][5u].value, 4);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][6u].value, 5);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][0u].value, 12);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][1u].value, 9);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][2u].value, 6);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][3u].value, 3);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][4u].value, 1);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][5u].value, 4);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][6u].value, 5);
ASSERT_EQ(group.get<char>(group.storage<boxed_int>().data()[0u]), 'e');
ASSERT_EQ(group.get<char>(group.storage<boxed_int>().data()[1u]), 'd');
ASSERT_EQ(group.get<char>(group.storage<boxed_int>().data()[2u]), 'c');
ASSERT_EQ(group.get<char>(group.storage<boxed_int>().data()[3u]), 'b');
ASSERT_EQ(group.get<char>(group.storage<boxed_int>().data()[4u]), 'a');
ASSERT_EQ(group.get<char>(group.handle().data()[0u]), 'e');
ASSERT_EQ(group.get<1>(group.handle().data()[1u]), 'd');
ASSERT_EQ(group.get<char>(group.handle().data()[2u]), 'c');
ASSERT_EQ(group.get<1>(group.handle().data()[3u]), 'b');
ASSERT_EQ(group.get<char>(group.handle().data()[4u]), 'a');
ASSERT_FALSE(group.contains(entities[5]));
ASSERT_FALSE(group.contains(entities[6]));
@ -1026,7 +1134,7 @@ TEST(OwningGroup, SortUnordered) {
TEST(OwningGroup, SortWithExclusionList) {
entt::registry registry;
auto group = registry.group<boxed_int>({}, entt::exclude<char>);
auto group = registry.group<boxed_int>(entt::get<>, entt::exclude<char>);
entt::entity entities[5]{};
registry.create(std::begin(entities), std::end(entities));
@ -1043,20 +1151,20 @@ TEST(OwningGroup, SortWithExclusionList) {
return lhs < rhs;
});
ASSERT_EQ(group.storage<boxed_int>().data()[0u], entities[4]);
ASSERT_EQ(group.storage<boxed_int>().data()[1u], entities[3]);
ASSERT_EQ(group.storage<boxed_int>().data()[2u], entities[1]);
ASSERT_EQ(group.storage<boxed_int>().data()[3u], entities[0]);
ASSERT_EQ(group.handle().data()[0u], entities[4]);
ASSERT_EQ(group.handle().data()[1u], entities[3]);
ASSERT_EQ(group.handle().data()[2u], entities[1]);
ASSERT_EQ(group.handle().data()[3u], entities[0]);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][0u].value, 4);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][1u].value, 3);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][2u].value, 1);
ASSERT_EQ(group.storage<boxed_int>().raw()[0u][3u].value, 0);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][0u].value, 4);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][1u].value, 3);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][2u].value, 1);
ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][3u].value, 0);
ASSERT_EQ(group.get<boxed_int>(entities[0]).value, 0);
ASSERT_EQ(group.get<boxed_int>(entities[1]).value, 1);
ASSERT_EQ(group.get<0>(entities[1]).value, 1);
ASSERT_EQ(group.get<boxed_int>(entities[3]).value, 3);
ASSERT_EQ(group.get<boxed_int>(entities[4]).value, 4);
ASSERT_EQ(group.get<0>(entities[4]).value, 4);
ASSERT_FALSE(group.contains(entities[2]));
}
@ -1110,11 +1218,24 @@ TEST(OwningGroup, ConstNonConstAndAllInBetween) {
ASSERT_EQ(group.size(), 1u);
static_assert(std::is_same_v<decltype(group.get<0>({})), int &>);
static_assert(std::is_same_v<decltype(group.get<int>({})), int &>);
static_assert(std::is_same_v<decltype(group.get<1>({})), const char &>);
static_assert(std::is_same_v<decltype(group.get<const char>({})), const char &>);
static_assert(std::is_same_v<decltype(group.get<2>({})), void>);
static_assert(std::is_same_v<decltype(group.get<empty_type>({})), void>);
static_assert(std::is_same_v<decltype(group.get<3>({})), double &>);
static_assert(std::is_same_v<decltype(group.get<double>({})), double &>);
static_assert(std::is_same_v<decltype(group.get<4>({})), const float &>);
static_assert(std::is_same_v<decltype(group.get<const float>({})), const float &>);
static_assert(std::is_same_v<decltype(group.get<int, const char, double, const float>({})), std::tuple<int &, const char &, double &, const float &>>);
static_assert(std::is_same_v<decltype(group.get<int, const char, empty_type, double, const float>({})), std::tuple<int &, const char &, double &, const float &>>);
static_assert(std::is_same_v<decltype(group.get<0, 1, 2, 3, 4>({})), std::tuple<int &, const char &, double &, const float &>>);
static_assert(std::is_same_v<decltype(group.get({})), std::tuple<int &, const char &, double &, const float &>>);
static_assert(std::is_same_v<decltype(std::as_const(registry).group_if_exists<int>(entt::get<char>)), decltype(std::as_const(registry).group_if_exists<const int>(entt::get<const char>))>);
@ -1192,7 +1313,7 @@ TEST(OwningGroup, ExcludedComponents) {
registry.emplace<int>(e1, 1);
registry.emplace<char>(e1);
const auto group = registry.group<int>({}, entt::exclude<char, double>);
const auto group = registry.group<int>(entt::get<>, entt::exclude<char, double>);
const auto e2 = registry.create();
registry.emplace<int>(e2, 2);
@ -1207,7 +1328,7 @@ TEST(OwningGroup, ExcludedComponents) {
if(entity == e0) {
ASSERT_EQ(group.get<int>(e0), 0);
} else if(entity == e2) {
ASSERT_EQ(group.get<int>(e2), 2);
ASSERT_EQ(group.get<0>(e2), 2);
}
}
@ -1225,7 +1346,7 @@ TEST(OwningGroup, ExcludedComponents) {
if(entity == e1) {
ASSERT_EQ(group.get<int>(e1), 1);
} else if(entity == e3) {
ASSERT_EQ(group.get<int>(e3), 3);
ASSERT_EQ(group.get<0>(e3), 3);
}
}
}
@ -1263,8 +1384,8 @@ TEST(OwningGroup, EmptyAndNonEmptyTypes) {
TEST(OwningGroup, TrackEntitiesOnComponentDestruction) {
entt::registry registry;
const auto group = registry.group<int>({}, entt::exclude<char>);
const auto cgroup = std::as_const(registry).group_if_exists<const int>({}, entt::exclude<char>);
const auto group = registry.group<int>(entt::get<>, entt::exclude<char>);
const auto cgroup = std::as_const(registry).group_if_exists<const int>(entt::get<>, entt::exclude<char>);
const auto entity = registry.create();
registry.emplace<int>(entity);
@ -1403,7 +1524,7 @@ TEST(OwningGroup, SwappingValuesIsAllowed) {
// thanks to @andranik3949 for pointing out this missing test
registry.view<const boxed_int>().each([](const auto entity, const auto &value) {
ASSERT_EQ(entt::to_integral(entity), value.value);
ASSERT_EQ(static_cast<int>(entt::to_integral(entity)), value.value);
});
}
@ -1443,26 +1564,81 @@ TEST(OwningGroup, IterableGroupAlgorithmCompatibility) {
TEST(OwningGroup, Storage) {
entt::registry registry;
const auto entity = registry.create();
const auto group = registry.group<int>(entt::get<const char>);
auto group = registry.group<int>(entt::get<const char>, entt::exclude<double, const float>);
static_assert(std::is_same_v<decltype(group.storage<0u>()), entt::storage_type_t<int> &>);
static_assert(std::is_same_v<decltype(group.storage<int>()), entt::storage_type_t<int> &>);
static_assert(std::is_same_v<decltype(group.storage<1u>()), const entt::storage_type_t<char> &>);
static_assert(std::is_same_v<decltype(group.storage<const char>()), const entt::storage_type_t<char> &>);
static_assert(std::is_same_v<decltype(group.storage<0u>()), entt::storage_type_t<int> *>);
static_assert(std::is_same_v<decltype(group.storage<int>()), entt::storage_type_t<int> *>);
static_assert(std::is_same_v<decltype(group.storage<const int>()), entt::storage_type_t<int> *>);
static_assert(std::is_same_v<decltype(group.storage<1u>()), const entt::storage_type_t<char> *>);
static_assert(std::is_same_v<decltype(group.storage<char>()), const entt::storage_type_t<char> *>);
static_assert(std::is_same_v<decltype(group.storage<const char>()), const entt::storage_type_t<char> *>);
static_assert(std::is_same_v<decltype(group.storage<2u>()), entt::storage_type_t<double> *>);
static_assert(std::is_same_v<decltype(group.storage<double>()), entt::storage_type_t<double> *>);
static_assert(std::is_same_v<decltype(group.storage<const double>()), entt::storage_type_t<double> *>);
static_assert(std::is_same_v<decltype(group.storage<3u>()), const entt::storage_type_t<float> *>);
static_assert(std::is_same_v<decltype(group.storage<float>()), const entt::storage_type_t<float> *>);
static_assert(std::is_same_v<decltype(group.storage<const float>()), const entt::storage_type_t<float> *>);
ASSERT_TRUE(group);
ASSERT_NE(group.storage<int>(), nullptr);
ASSERT_NE(group.storage<1u>(), nullptr);
ASSERT_NE(group.storage<double>(), nullptr);
ASSERT_NE(group.storage<3u>(), nullptr);
ASSERT_EQ(group.size(), 0u);
group.storage<int>().emplace(entity);
group.storage<int>()->emplace(entity);
group.storage<double>()->emplace(entity);
registry.emplace<char>(entity);
registry.emplace<float>(entity);
ASSERT_EQ(group.size(), 0u);
ASSERT_EQ(group.begin(), group.end());
ASSERT_TRUE(group.storage<int>()->contains(entity));
ASSERT_TRUE(group.storage<const char>()->contains(entity));
ASSERT_TRUE(group.storage<double>()->contains(entity));
ASSERT_TRUE(group.storage<const float>()->contains(entity));
ASSERT_TRUE((registry.all_of<int, char, double, float>(entity)));
group.storage<double>()->erase(entity);
registry.erase<float>(entity);
ASSERT_EQ(group.size(), 1u);
ASSERT_TRUE(group.storage<int>().contains(entity));
ASSERT_TRUE(group.storage<const char>().contains(entity));
ASSERT_NE(group.begin(), group.end());
ASSERT_TRUE(group.storage<const int>()->contains(entity));
ASSERT_TRUE(group.storage<char>()->contains(entity));
ASSERT_FALSE(group.storage<const double>()->contains(entity));
ASSERT_FALSE(group.storage<float>()->contains(entity));
ASSERT_TRUE((registry.all_of<int, char>(entity)));
ASSERT_FALSE((registry.any_of<double, float>(entity)));
group.storage<0u>().erase(entity);
group.storage<0u>()->erase(entity);
ASSERT_EQ(group.size(), 0u);
ASSERT_TRUE(group.storage<1u>().contains(entity));
ASSERT_FALSE((registry.all_of<int, char>(entity)));
ASSERT_EQ(group.begin(), group.end());
ASSERT_FALSE(group.storage<0u>()->contains(entity));
ASSERT_TRUE(group.storage<1u>()->contains(entity));
ASSERT_FALSE(group.storage<2u>()->contains(entity));
ASSERT_FALSE(group.storage<3u>()->contains(entity));
ASSERT_TRUE((registry.all_of<char>(entity)));
ASSERT_FALSE((registry.any_of<int, double, float>(entity)));
group = {};
ASSERT_FALSE(group);
ASSERT_EQ(group.storage<0u>(), nullptr);
ASSERT_EQ(group.storage<const char>(), nullptr);
ASSERT_EQ(group.storage<2u>(), nullptr);
ASSERT_EQ(group.storage<const float>(), nullptr);
}
ENTT_DEBUG_TEST(OwningGroupDeathTest, Overlapping) {
entt::registry registry;
registry.group<char>(entt::get<int>, entt::exclude<double>);
ASSERT_DEATH((registry.group<char, float>(entt::get<float>, entt::exclude<double>)), "");
ASSERT_DEATH(registry.group<char>(entt::get<int, float>, entt::exclude<double>), "");
ASSERT_DEATH(registry.group<char>(entt::get<int>, entt::exclude<double, float>), "");
}

View File

@ -83,8 +83,8 @@ TEST(BasicHandle, Destruction) {
ASSERT_FALSE(handle);
ASSERT_FALSE(handle.valid());
ASSERT_NE(handle.registry(), nullptr);
ASSERT_EQ(handle.entity(), entity);
ASSERT_EQ(registry.current(entity), typename entt::registry::version_type{});
ASSERT_EQ(handle.entity(), entt::entity{entt::null});
handle = entt::handle{registry, registry.create()};
@ -98,8 +98,8 @@ TEST(BasicHandle, Destruction) {
ASSERT_FALSE(handle);
ASSERT_FALSE(handle.valid());
ASSERT_NE(handle.registry(), nullptr);
ASSERT_EQ(handle.entity(), entity);
ASSERT_NE(registry.current(entity), typename entt::registry::version_type{});
ASSERT_EQ(handle.entity(), entt::entity{entt::null});
}
TEST(BasicHandle, Comparison) {
@ -275,6 +275,8 @@ TEST(BasicHandle, HandleStorageIterator) {
registry.emplace<int>(entity);
registry.emplace<double>(entity);
// required to test the find-first initialization step
registry.storage<entt::entity>().erase(entity);
auto test = [](auto iterable) {
auto end{iterable.begin()};
@ -290,6 +292,13 @@ TEST(BasicHandle, HandleStorageIterator) {
ASSERT_EQ(++begin, iterable.end());
};
test(entt::handle{registry, entity}.storage());
test(entt::const_handle{std::as_const(registry), entity}.storage());
const auto handle = entt::handle{registry, entity};
const auto chandle = entt::const_handle{std::as_const(registry), entity};
ASSERT_FALSE(registry.valid(entity));
ASSERT_FALSE(handle);
ASSERT_FALSE(chandle);
test(handle.storage());
test(chandle.storage());
}

View File

@ -16,6 +16,10 @@ struct stable_type {
int value;
};
void sigh_callback(int &value) {
++value;
}
TEST(Helper, AsView) {
entt::registry registry;
const entt::registry cregistry;
@ -48,7 +52,7 @@ TEST(Helper, Invoke) {
TEST(Helper, ToEntity) {
entt::registry registry;
const entt::entity null = entt::null;
constexpr auto page_size = entt::component_traits<int>::page_size;
constexpr auto page_size = entt::storage_type_t<int>::traits_type::page_size;
const int value = 42;
ASSERT_EQ(entt::to_entity(registry, 42), null);
@ -88,7 +92,7 @@ TEST(Helper, ToEntity) {
TEST(Helper, ToEntityStableType) {
entt::registry registry;
const entt::entity null = entt::null;
constexpr auto page_size = entt::component_traits<stable_type>::page_size;
constexpr auto page_size = entt::storage_type_t<stable_type>::traits_type::page_size;
const stable_type value{42};
ASSERT_EQ(entt::to_entity(registry, stable_type{42}), null);
@ -124,3 +128,44 @@ TEST(Helper, ToEntityStableType) {
ASSERT_EQ(entt::to_entity(registry, stable_type{42}), null);
ASSERT_EQ(entt::to_entity(registry, value), null);
}
TEST(Helper, SighHelper) {
using namespace entt::literals;
entt::registry registry{};
const auto entt = registry.create();
entt::sigh_helper helper{registry};
int counter{};
ASSERT_EQ(&helper.registry(), &registry);
helper.with<int>()
.on_construct<&sigh_callback>(counter)
.on_update<&sigh_callback>(counter)
.on_destroy<&sigh_callback>(counter);
ASSERT_EQ(counter, 0);
registry.emplace<int>(entt);
registry.replace<int>(entt);
registry.erase<int>(entt);
ASSERT_EQ(counter, 3);
helper.with<double>("other"_hs)
.on_construct<&sigh_callback>(counter)
.on_update<&sigh_callback>(counter)
.on_destroy<&sigh_callback>(counter);
registry.emplace<double>(entt);
registry.replace<double>(entt);
registry.erase<double>(entt);
ASSERT_EQ(counter, 3);
registry.storage<double>("other"_hs).emplace(entt);
registry.storage<double>("other"_hs).patch(entt);
registry.storage<double>("other"_hs).erase(entt);
ASSERT_EQ(counter, 6);
}

View File

@ -1,5 +1,7 @@
#include <cstddef>
#include <utility>
#include <gtest/gtest.h>
#include <entt/core/type_info.hpp>
#include <entt/entity/organizer.hpp>
#include <entt/entity/registry.hpp>
@ -20,7 +22,7 @@ struct clazz {
static void ro_int_char_with_payload(clazz &, entt::view<entt::get_t<const int, const char>>) {}
};
void to_args_integrity(entt::view<entt::get_t<int>> view, std::size_t &value, entt::registry &registry) {
void to_args_integrity(entt::view<entt::get_t<int>> view, std::size_t &value, entt::registry &) {
value = view.size();
}
@ -363,6 +365,10 @@ TEST(Organizer, Prepare) {
ASSERT_FALSE(registry.ctx().contains<char>());
ASSERT_FALSE(registry.ctx().contains<double>());
ASSERT_EQ(std::as_const(registry).storage<int>(), nullptr);
ASSERT_EQ(std::as_const(registry).storage<char>(), nullptr);
ASSERT_EQ(std::as_const(registry).storage<double>(), nullptr);
for(auto &&vertex: graph) {
vertex.prepare(registry);
}
@ -370,6 +376,10 @@ TEST(Organizer, Prepare) {
ASSERT_FALSE(registry.ctx().contains<int>());
ASSERT_FALSE(registry.ctx().contains<char>());
ASSERT_TRUE(registry.ctx().contains<double>());
ASSERT_NE(std::as_const(registry).storage<int>(), nullptr);
ASSERT_NE(std::as_const(registry).storage<char>(), nullptr);
ASSERT_EQ(std::as_const(registry).storage<double>(), nullptr);
}
TEST(Organizer, Dependencies) {
@ -428,5 +438,5 @@ TEST(Organizer, ToArgsIntegrity) {
auto graph = organizer.graph();
graph[0u].callback()(graph[0u].data(), registry);
ASSERT_EQ(registry.ctx().at<std::size_t>(), 0u);
ASSERT_EQ(registry.ctx().get<std::size_t>(), 0u);
}

View File

@ -40,18 +40,16 @@ struct aggregate {
};
struct listener {
template<typename Component>
template<typename Type>
static void sort(entt::registry &registry) {
registry.sort<Component>([](auto lhs, auto rhs) { return lhs < rhs; });
registry.sort<Type>([](auto lhs, auto rhs) { return lhs < rhs; });
}
template<typename Component>
void incr(const entt::registry &, entt::entity entity) {
last = entity;
++counter;
}
template<typename Component>
void decr(const entt::registry &, entt::entity entity) {
last = entity;
--counter;
@ -75,11 +73,11 @@ struct destruction_order {
destruction_order(const entt::registry &ref, bool &ctx)
: registry{&ref},
ctx_check{&ctx} {
*ctx_check = (registry->ctx().find<int>() != nullptr);
*ctx_check = (registry->ctx().find<ctx_check_type>() != nullptr);
}
~destruction_order() {
*ctx_check = *ctx_check && (registry->ctx().find<int>() != nullptr);
*ctx_check = *ctx_check && (registry->ctx().find<ctx_check_type>() != nullptr);
}
private:
@ -87,6 +85,22 @@ private:
bool *ctx_check{};
};
enum class small_entity : std::uint32_t {};
struct small_entity_traits {
using value_type = small_entity;
using entity_type = uint32_t;
using version_type = uint16_t;
static constexpr entity_type entity_mask = 0xFF;
static constexpr entity_type version_mask = 0x00;
};
template<>
struct entt::entt_traits<small_entity>: entt::basic_entt_traits<small_entity_traits> {
using base_type = entt::basic_entt_traits<small_entity_traits>;
static constexpr auto page_size = ENTT_SPARSE_PAGE;
};
TEST(Registry, Context) {
entt::registry registry;
auto &ctx = registry.ctx();
@ -126,8 +140,8 @@ TEST(Registry, Context) {
ASSERT_EQ(ctx.emplace<const int>(0), 42);
ASSERT_EQ(ctx.find<const int>(), cctx.find<int>());
ASSERT_EQ(ctx.at<int>(), cctx.at<const int>());
ASSERT_EQ(ctx.at<int>(), 42);
ASSERT_EQ(ctx.get<int>(), cctx.get<const int>());
ASSERT_EQ(ctx.get<int>(), 42);
ASSERT_EQ(ctx.find<double>(), nullptr);
ASSERT_EQ(cctx.find<double>(), nullptr);
@ -139,8 +153,8 @@ TEST(Registry, Context) {
ASSERT_EQ(ctx.insert_or_assign<const int>(0), 0);
ASSERT_EQ(ctx.find<const int>(), cctx.find<int>());
ASSERT_EQ(ctx.at<int>(), cctx.at<const int>());
ASSERT_EQ(ctx.at<int>(), 0);
ASSERT_EQ(ctx.get<int>(), cctx.get<const int>());
ASSERT_EQ(ctx.get<int>(), 0);
}
TEST(Registry, ContextHint) {
@ -376,13 +390,12 @@ TEST(Registry, Functionalities) {
TEST(Registry, Constructors) {
entt::registry registry;
entt::registry other{42};
const entt::entity entity = entt::tombstone;
ASSERT_TRUE(registry.empty());
ASSERT_TRUE(other.empty());
ASSERT_EQ(registry.released(), entity);
ASSERT_EQ(other.released(), entity);
ASSERT_EQ(registry.released(), 0u);
ASSERT_EQ(other.released(), 0u);
}
TEST(Registry, Move) {
@ -486,6 +499,8 @@ TEST(Registry, Identifiers) {
}
TEST(Registry, Data) {
using traits_type = entt::entt_traits<entt::entity>;
entt::registry registry;
ASSERT_EQ(std::as_const(registry).data(), nullptr);
@ -497,8 +512,8 @@ TEST(Registry, Data) {
const auto other = registry.create();
registry.release(entity);
ASSERT_NE(*std::as_const(registry).data(), entity);
ASSERT_EQ(*(std::as_const(registry).data() + 1u), other);
ASSERT_EQ(*std::as_const(registry).data(), other);
ASSERT_EQ(*(std::as_const(registry).data() + 1u), traits_type::next(entity));
}
TEST(Registry, CreateManyEntitiesAtOnce) {
@ -533,7 +548,7 @@ TEST(Registry, CreateManyEntitiesAtOnceWithListener) {
entt::entity entities[3];
listener listener;
registry.on_construct<int>().connect<&listener::incr<int>>(listener);
registry.on_construct<int>().connect<&listener::incr>(listener);
registry.create(std::begin(entities), std::end(entities));
registry.insert(std::begin(entities), std::end(entities), 42);
registry.insert(std::begin(entities), std::end(entities), 'c');
@ -542,8 +557,8 @@ TEST(Registry, CreateManyEntitiesAtOnceWithListener) {
ASSERT_EQ(registry.get<char>(entities[1]), 'c');
ASSERT_EQ(listener.counter, 3);
registry.on_construct<int>().disconnect<&listener::incr<int>>(listener);
registry.on_construct<empty_type>().connect<&listener::incr<empty_type>>(listener);
registry.on_construct<int>().disconnect<&listener::incr>(listener);
registry.on_construct<empty_type>().connect<&listener::incr>(listener);
registry.create(std::begin(entities), std::end(entities));
registry.insert(std::begin(entities), std::end(entities), 'a');
registry.insert<empty_type>(std::begin(entities), std::end(entities));
@ -560,8 +575,9 @@ TEST(Registry, CreateWithHint) {
auto e3 = registry.create(entt::entity{3});
auto e2 = registry.create(entt::entity{3});
ASSERT_EQ(e2, entt::entity{2});
ASSERT_FALSE(registry.valid(entt::entity{1}));
ASSERT_EQ(e2, entt::entity{1});
ASSERT_FALSE(registry.valid(entt::entity{0}));
ASSERT_FALSE(registry.valid(entt::entity{2}));
ASSERT_EQ(e3, entt::entity{3});
registry.release(e2);
@ -572,10 +588,10 @@ TEST(Registry, CreateWithHint) {
e2 = registry.create();
auto e1 = registry.create(entt::entity{2});
ASSERT_EQ(traits_type::to_entity(e2), 2u);
ASSERT_EQ(traits_type::to_entity(e2), 1u);
ASSERT_EQ(traits_type::to_version(e2), 1u);
ASSERT_EQ(traits_type::to_entity(e1), 1u);
ASSERT_EQ(traits_type::to_entity(e1), 2u);
ASSERT_EQ(traits_type::to_version(e1), 0u);
registry.release(e1);
@ -640,6 +656,14 @@ TEST(Registry, CreateDestroyReleaseCornerCase) {
ASSERT_EQ(registry.current(e1), 1u);
}
ENTT_DEBUG_TEST(RegistryDeathTest, CreateTooManyEntities) {
entt::basic_registry<small_entity> registry;
std::vector<small_entity> entities(entt::entt_traits<small_entity>::to_entity(entt::null));
registry.create(entities.begin(), entities.end());
ASSERT_DEATH([[maybe_unused]] const auto entity = registry.create(), "");
}
TEST(Registry, DestroyVersion) {
entt::registry registry;
@ -666,7 +690,7 @@ ENTT_DEBUG_TEST(RegistryDeathTest, DestroyVersion) {
ASSERT_DEATH(registry.destroy(entity, 3), "");
}
TEST(Registry, RangeDestroy) {
TEST(Registry, DestroyRange) {
entt::registry registry;
const auto iview = registry.view<int>();
const auto icview = registry.view<int, char>();
@ -722,6 +746,24 @@ TEST(Registry, RangeDestroy) {
ASSERT_FALSE(registry.valid(entities[1u]));
ASSERT_FALSE(registry.valid(entities[2u]));
ASSERT_EQ(registry.storage<int>().size(), 0u);
entt::sparse_set managed{};
registry.create(std::begin(entities), std::end(entities));
managed.push(std::begin(entities), std::end(entities));
registry.insert<int>(managed.begin(), managed.end());
ASSERT_TRUE(registry.valid(managed[0u]));
ASSERT_TRUE(registry.valid(managed[1u]));
ASSERT_TRUE(registry.valid(managed[2u]));
ASSERT_EQ(registry.storage<int>().size(), 3u);
registry.destroy(managed.begin(), managed.end());
ASSERT_FALSE(registry.valid(managed[0u]));
ASSERT_FALSE(registry.valid(managed[1u]));
ASSERT_FALSE(registry.valid(managed[2u]));
ASSERT_EQ(registry.storage<int>().size(), 0u);
}
TEST(Registry, StableDestroy) {
@ -792,7 +834,7 @@ ENTT_DEBUG_TEST(RegistryDeathTest, ReleaseVersion) {
ASSERT_DEATH(registry.release(entity, 3), "");
}
TEST(Registry, RangeRelease) {
TEST(Registry, ReleaseRange) {
entt::registry registry;
entt::entity entities[3u];
@ -930,10 +972,12 @@ TEST(Registry, Orphans) {
TEST(Registry, View) {
entt::registry registry;
auto mview = registry.view<int, char>();
entt::entity entities[3u];
auto iview = registry.view<int>();
auto cview = registry.view<char>();
entt::entity entities[3u];
auto mview = registry.view<int, char>();
auto fview = registry.view<int>(entt::exclude<char>);
registry.create(std::begin(entities), std::end(entities));
@ -948,10 +992,55 @@ TEST(Registry, View) {
ASSERT_EQ(iview.size(), 3u);
ASSERT_EQ(cview.size(), 2u);
std::size_t cnt{};
mview.each([&cnt](auto...) { ++cnt; });
ASSERT_EQ(mview.size_hint(), 3u);
ASSERT_EQ(fview.size_hint(), 3u);
ASSERT_EQ(cnt, 2u);
mview.refresh();
fview.refresh();
ASSERT_EQ(mview.size_hint(), 2u);
ASSERT_EQ(fview.size_hint(), 3u);
ASSERT_NE(mview.begin(), mview.end());
ASSERT_NE(fview.begin(), fview.end());
ASSERT_EQ(std::distance(mview.begin(), mview.end()), 2);
ASSERT_EQ(std::distance(fview.begin(), fview.end()), 1);
mview.each([&entities, first = true](auto entity, auto &&...) mutable {
ASSERT_EQ(entity, entities[2u * first]);
first = false;
});
fview.each([&entities](auto entity, auto &&...) {
ASSERT_EQ(entity, entities[1u]);
});
}
TEST(Registry, ExcludeOnlyView) {
entt::registry registry;
entt::entity entities[4u];
auto view = registry.view<entt::entity>(entt::exclude<int>);
registry.create(std::begin(entities), std::end(entities));
registry.emplace<int>(entities[0u], 0);
registry.emplace<int>(entities[2u], 0);
registry.emplace<int>(entities[3u], 0);
registry.destroy(entities[3u]);
ASSERT_EQ(view.size_hint(), 4u);
ASSERT_NE(view.begin(), view.end());
// returns all matching identifiers, both in-use and available ones
ASSERT_EQ(std::distance(view.begin(), view.end()), 2);
// skips available identifiers automatically, only returns in-use elements
view.each([&entities](auto entity, auto &&...) {
ASSERT_EQ(entity, entities[1u]);
});
}
TEST(Registry, NonOwningGroupInitOnFirstUse) {
@ -964,7 +1053,7 @@ TEST(Registry, NonOwningGroupInitOnFirstUse) {
registry.emplace<char>(entities[2u], 'c');
std::size_t cnt{};
auto group = registry.group<>(entt::get<int, char>);
auto group = registry.group(entt::get<int, char>);
group.each([&cnt](auto...) { ++cnt; });
ASSERT_FALSE((registry.owned<int, char>()));
@ -974,7 +1063,7 @@ TEST(Registry, NonOwningGroupInitOnFirstUse) {
TEST(Registry, NonOwningGroupInitOnEmplace) {
entt::registry registry;
entt::entity entities[3u];
auto group = registry.group<>(entt::get<int, char>);
auto group = registry.group(entt::get<int, char>);
registry.create(std::begin(entities), std::end(entities));
registry.insert<int>(std::begin(entities), std::end(entities), 0);
@ -1097,7 +1186,7 @@ TEST(Registry, CleanViewAfterRemoveAndClear) {
TEST(Registry, CleanNonOwningGroupViewAfterRemoveAndClear) {
entt::registry registry;
auto group = registry.group<>(entt::get<int, char>);
auto group = registry.group(entt::get<int, char>);
const auto entity = registry.create();
registry.emplace<int>(entity, 0);
@ -1188,107 +1277,21 @@ TEST(Registry, CleanPartialOwningGroupViewAfterRemoveAndClear) {
ASSERT_EQ(group.size(), 0u);
}
TEST(Registry, NestedGroups) {
ENTT_DEBUG_TEST(RegistryDeathTest, NestedGroups) {
entt::registry registry;
entt::entity entities[10];
registry.group<int, double>(entt::get<char>);
registry.create(std::begin(entities), std::end(entities));
registry.insert<int>(std::begin(entities), std::end(entities));
registry.insert<char>(std::begin(entities), std::end(entities));
const auto g1 = registry.group<int>(entt::get<char>, entt::exclude<double>);
ASSERT_DEATH(registry.group<int>(entt::get<char>), "");
ASSERT_DEATH(registry.group<int>(entt::get<char, double>), "");
ASSERT_DEATH(registry.group<int>(entt::get<char>, entt::exclude<double>), "");
ASSERT_DEATH((registry.group<int, double>()), "");
}
ASSERT_TRUE(registry.sortable(g1));
ASSERT_EQ(g1.size(), 10u);
ENTT_DEBUG_TEST(RegistryDeathTest, ConflictingGroups) {
entt::registry registry;
const auto g2 = registry.group<int>(entt::get<char>);
ASSERT_TRUE(registry.sortable(g1));
ASSERT_FALSE(registry.sortable(g2));
ASSERT_EQ(g1.size(), 10u);
ASSERT_EQ(g2.size(), 10u);
for(auto i = 0u; i < 5u; ++i) {
ASSERT_TRUE(g1.contains(entities[i * 2 + 1]));
ASSERT_TRUE(g1.contains(entities[i * 2]));
ASSERT_TRUE(g2.contains(entities[i * 2 + 1]));
ASSERT_TRUE(g2.contains(entities[i * 2]));
registry.emplace<double>(entities[i * 2]);
}
ASSERT_EQ(g1.size(), 5u);
ASSERT_EQ(g2.size(), 10u);
for(auto i = 0u; i < 5u; ++i) {
ASSERT_TRUE(g1.contains(entities[i * 2 + 1]));
ASSERT_FALSE(g1.contains(entities[i * 2]));
ASSERT_TRUE(g2.contains(entities[i * 2 + 1]));
ASSERT_TRUE(g2.contains(entities[i * 2]));
registry.erase<int>(entities[i * 2 + 1]);
}
ASSERT_EQ(g1.size(), 0u);
ASSERT_EQ(g2.size(), 5u);
const auto g3 = registry.group<int, float>(entt::get<char>, entt::exclude<double>);
ASSERT_FALSE(registry.sortable(g1));
ASSERT_FALSE(registry.sortable(g2));
ASSERT_TRUE(registry.sortable(g3));
ASSERT_EQ(g1.size(), 0u);
ASSERT_EQ(g2.size(), 5u);
ASSERT_EQ(g3.size(), 0u);
for(auto i = 0u; i < 5u; ++i) {
ASSERT_FALSE(g1.contains(entities[i * 2 + 1]));
ASSERT_FALSE(g1.contains(entities[i * 2]));
ASSERT_FALSE(g2.contains(entities[i * 2 + 1]));
ASSERT_TRUE(g2.contains(entities[i * 2]));
ASSERT_FALSE(g3.contains(entities[i * 2 + 1]));
ASSERT_FALSE(g3.contains(entities[i * 2]));
registry.emplace<int>(entities[i * 2 + 1]);
}
ASSERT_EQ(g1.size(), 5u);
ASSERT_EQ(g2.size(), 10u);
ASSERT_EQ(g3.size(), 0u);
for(auto i = 0u; i < 5u; ++i) {
ASSERT_TRUE(g1.contains(entities[i * 2 + 1]));
ASSERT_FALSE(g1.contains(entities[i * 2]));
ASSERT_TRUE(g2.contains(entities[i * 2 + 1]));
ASSERT_TRUE(g2.contains(entities[i * 2]));
ASSERT_FALSE(g3.contains(entities[i * 2 + 1]));
ASSERT_FALSE(g3.contains(entities[i * 2]));
registry.emplace<float>(entities[i * 2]);
}
ASSERT_EQ(g1.size(), 5u);
ASSERT_EQ(g2.size(), 10u);
ASSERT_EQ(g3.size(), 0u);
for(auto i = 0u; i < 5u; ++i) {
registry.erase<double>(entities[i * 2]);
}
ASSERT_EQ(g1.size(), 10u);
ASSERT_EQ(g2.size(), 10u);
ASSERT_EQ(g3.size(), 5u);
for(auto i = 0u; i < 5u; ++i) {
ASSERT_TRUE(g1.contains(entities[i * 2 + 1]));
ASSERT_TRUE(g1.contains(entities[i * 2]));
ASSERT_TRUE(g2.contains(entities[i * 2 + 1]));
ASSERT_TRUE(g2.contains(entities[i * 2]));
ASSERT_FALSE(g3.contains(entities[i * 2 + 1]));
ASSERT_TRUE(g3.contains(entities[i * 2]));
registry.erase<int>(entities[i * 2 + 1]);
registry.erase<int>(entities[i * 2]);
}
ASSERT_EQ(g1.size(), 0u);
ASSERT_EQ(g2.size(), 0u);
ASSERT_EQ(g3.size(), 0u);
registry.group<char>(entt::get<int>, entt::exclude<double>);
ASSERT_DEATH(registry.group<char>(entt::get<float>, entt::exclude<double>), "");
}
TEST(Registry, SortSingle) {
@ -1379,10 +1382,10 @@ TEST(Registry, Signals) {
entt::entity entities[2u];
listener listener;
registry.on_construct<empty_type>().connect<&listener::incr<empty_type>>(listener);
registry.on_destroy<empty_type>().connect<&listener::decr<empty_type>>(listener);
registry.on_construct<int>().connect<&listener::incr<int>>(listener);
registry.on_destroy<int>().connect<&listener::decr<int>>(listener);
registry.on_construct<empty_type>().connect<&listener::incr>(listener);
registry.on_destroy<empty_type>().connect<&listener::decr>(listener);
registry.on_construct<int>().connect<&listener::incr>(listener);
registry.on_destroy<int>().connect<&listener::decr>(listener);
registry.create(std::begin(entities), std::end(entities));
registry.insert<empty_type>(std::begin(entities), std::end(entities));
@ -1400,16 +1403,16 @@ TEST(Registry, Signals) {
ASSERT_EQ(listener.counter, 2);
ASSERT_EQ(listener.last, entities[0u]);
registry.on_destroy<empty_type>().disconnect<&listener::decr<empty_type>>(listener);
registry.on_destroy<int>().disconnect<&listener::decr<int>>(listener);
registry.on_destroy<empty_type>().disconnect<&listener::decr>(listener);
registry.on_destroy<int>().disconnect<&listener::decr>(listener);
registry.erase<empty_type, int>(entities[1u]);
ASSERT_EQ(listener.counter, 2);
ASSERT_EQ(listener.last, entities[0u]);
registry.on_construct<empty_type>().disconnect<&listener::incr<empty_type>>(listener);
registry.on_construct<int>().disconnect<&listener::incr<int>>(listener);
registry.on_construct<empty_type>().disconnect<&listener::incr>(listener);
registry.on_construct<int>().disconnect<&listener::incr>(listener);
registry.emplace<empty_type>(entities[1u]);
registry.emplace<int>(entities[1u]);
@ -1417,8 +1420,8 @@ TEST(Registry, Signals) {
ASSERT_EQ(listener.counter, 2);
ASSERT_EQ(listener.last, entities[0u]);
registry.on_construct<int>().connect<&listener::incr<int>>(listener);
registry.on_destroy<int>().connect<&listener::decr<int>>(listener);
registry.on_construct<int>().connect<&listener::incr>(listener);
registry.on_destroy<int>().connect<&listener::decr>(listener);
registry.emplace<int>(entities[0u]);
registry.erase<int>(entities[1u]);
@ -1426,8 +1429,8 @@ TEST(Registry, Signals) {
ASSERT_EQ(listener.counter, 2);
ASSERT_EQ(listener.last, entities[1u]);
registry.on_construct<empty_type>().connect<&listener::incr<empty_type>>(listener);
registry.on_destroy<empty_type>().connect<&listener::decr<empty_type>>(listener);
registry.on_construct<empty_type>().connect<&listener::incr>(listener);
registry.on_destroy<empty_type>().connect<&listener::decr>(listener);
registry.erase<empty_type>(entities[1u]);
registry.emplace<empty_type>(entities[0u]);
@ -1454,8 +1457,8 @@ TEST(Registry, Signals) {
ASSERT_EQ(listener.counter, 2);
ASSERT_EQ(listener.last, entities[0u]);
registry.on_destroy<empty_type>().disconnect<&listener::decr<empty_type>>(listener);
registry.on_destroy<int>().disconnect<&listener::decr<int>>(listener);
registry.on_destroy<empty_type>().disconnect<&listener::decr>(listener);
registry.on_destroy<int>().disconnect<&listener::decr>(listener);
registry.emplace_or_replace<empty_type>(entities[0u]);
registry.emplace_or_replace<int>(entities[0u]);
@ -1463,8 +1466,8 @@ TEST(Registry, Signals) {
ASSERT_EQ(listener.counter, 2);
ASSERT_EQ(listener.last, entities[0u]);
registry.on_update<empty_type>().connect<&listener::incr<empty_type>>(listener);
registry.on_update<int>().connect<&listener::incr<int>>(listener);
registry.on_update<empty_type>().connect<&listener::incr>(listener);
registry.on_update<int>().connect<&listener::incr>(listener);
registry.emplace_or_replace<empty_type>(entities[0u]);
registry.emplace_or_replace<int>(entities[0u]);
@ -1479,6 +1482,84 @@ TEST(Registry, Signals) {
ASSERT_EQ(listener.last, entities[0u]);
}
TEST(Registry, SignalsOnRuntimePool) {
using namespace entt::literals;
entt::registry registry;
const auto entity = registry.create();
listener listener;
registry.on_construct<int>("custom"_hs).connect<&listener::incr>(listener);
registry.on_update<int>("custom"_hs).connect<&listener::incr>(listener);
registry.on_destroy<int>("custom"_hs).connect<&listener::incr>(listener);
ASSERT_EQ(listener.counter, 0);
registry.emplace<int>(entity);
registry.patch<int>(entity);
registry.erase<int>(entity);
ASSERT_EQ(listener.counter, 0);
registry.storage<int>("custom"_hs).emplace(entity);
registry.storage<int>("custom"_hs).patch(entity);
registry.storage<int>("custom"_hs).erase(entity);
ASSERT_EQ(listener.counter, 3);
}
TEST(Registry, SignalsOnEntity) {
entt::registry registry;
listener listener;
registry.on_construct<entt::entity>().connect<&listener::incr>(listener);
entt::entity entity = registry.create();
entt::entity other = registry.create();
ASSERT_EQ(listener.counter, 2);
ASSERT_EQ(listener.last, other);
registry.destroy(other);
registry.destroy(entity);
ASSERT_EQ(listener.counter, 2);
ASSERT_EQ(listener.last, other);
registry.on_construct<entt::entity>().disconnect(&listener);
other = registry.create();
entity = registry.create();
ASSERT_EQ(listener.counter, 2);
ASSERT_NE(listener.last, entity);
ASSERT_NE(listener.last, other);
registry.on_update<entt::entity>().connect<&listener::decr>(listener);
registry.patch<entt::entity>(entity);
ASSERT_EQ(listener.counter, 1);
ASSERT_EQ(listener.last, entity);
registry.on_update<entt::entity>().disconnect(&listener);
registry.patch<entt::entity>(other);
ASSERT_EQ(listener.counter, 1);
ASSERT_NE(listener.last, other);
registry.on_destroy<entt::entity>().connect<&listener::decr>(listener);
registry.destroy(entity);
ASSERT_EQ(listener.counter, 0);
ASSERT_EQ(listener.last, entity);
registry.on_destroy<entt::entity>().disconnect(&listener);
registry.destroy(other);
ASSERT_EQ(listener.counter, 0);
ASSERT_NE(listener.last, other);
}
TEST(Registry, SignalWhenDestroying) {
entt::registry registry;
const auto entity = registry.create();
@ -1576,6 +1657,17 @@ TEST(Registry, Erase) {
ASSERT_EQ(registry.storage<char>().size(), 0u);
ASSERT_EQ(registry.storage<double>().size(), 1u);
registry.insert<int>(std::begin(entities) + 1, std::end(entities) - 1u);
registry.insert<char>(std::begin(entities) + 1, std::end(entities) - 1u);
ASSERT_EQ(registry.storage<int>().size(), 1u);
ASSERT_EQ(registry.storage<char>().size(), 1u);
registry.erase<int, char>(iview.begin(), iview.end());
ASSERT_EQ(registry.storage<int>().size(), 0u);
ASSERT_EQ(registry.storage<char>().size(), 0u);
registry.insert<int>(std::begin(entities), std::end(entities));
registry.insert<char>(std::begin(entities), std::end(entities));
@ -1643,6 +1735,39 @@ TEST(Registry, StableErase) {
ASSERT_EQ(registry.storage<double>().size(), 1u);
}
TEST(Registry, EraseIf) {
using namespace entt::literals;
entt::registry registry;
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.storage<int>("other"_hs).emplace(entity);
registry.emplace<char>(entity);
ASSERT_TRUE(registry.storage<int>().contains(entity));
ASSERT_TRUE(registry.storage<int>("other"_hs).contains(entity));
ASSERT_TRUE(registry.storage<char>().contains(entity));
registry.erase_if(entity, [](auto &&...) { return false; });
ASSERT_TRUE(registry.storage<int>().contains(entity));
ASSERT_TRUE(registry.storage<int>("other"_hs).contains(entity));
ASSERT_TRUE(registry.storage<char>().contains(entity));
registry.erase_if(entity, [](entt::id_type id, auto &&...) { return id == "other"_hs; });
ASSERT_TRUE(registry.storage<int>().contains(entity));
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>(); });
ASSERT_TRUE(registry.storage<int>().contains(entity));
ASSERT_FALSE(registry.storage<int>("other"_hs).contains(entity));
ASSERT_FALSE(registry.storage<char>().contains(entity));
}
TEST(Registry, Remove) {
entt::registry registry;
const auto iview = registry.view<int>();
@ -1689,6 +1814,18 @@ TEST(Registry, Remove) {
ASSERT_EQ(registry.storage<char>().size(), 0u);
ASSERT_EQ(registry.storage<double>().size(), 1u);
registry.insert<int>(std::begin(entities) + 1, std::end(entities) - 1u);
registry.insert<char>(std::begin(entities) + 1, std::end(entities) - 1u);
ASSERT_EQ(registry.storage<int>().size(), 1u);
ASSERT_EQ(registry.storage<char>().size(), 1u);
registry.remove<int, char>(iview.begin(), iview.end());
registry.remove<int, char>(iview.begin(), iview.end());
ASSERT_EQ(registry.storage<int>().size(), 0u);
ASSERT_EQ(registry.storage<char>().size(), 0u);
registry.insert<int>(std::begin(entities), std::end(entities));
registry.insert<char>(std::begin(entities), std::end(entities));
@ -1791,7 +1928,7 @@ TEST(Registry, NonOwningGroupInterleaved) {
registry.emplace<int>(entity);
registry.emplace<char>(entity);
const auto group = registry.group<>(entt::get<int, char>);
const auto group = registry.group(entt::get<int, char>);
entity = registry.create();
registry.emplace<int>(entity);
@ -1845,7 +1982,7 @@ TEST(Registry, PartialOwningGroupInterleaved) {
TEST(Registry, NonOwningGroupSortInterleaved) {
entt::registry registry;
const auto group = registry.group<>(entt::get<int, char>);
const auto group = registry.group(entt::get<int, char>);
const auto e0 = registry.create();
registry.emplace<int>(e0, 0);
@ -2009,7 +2146,7 @@ TEST(Registry, ScramblingPoolsIsAllowed) {
// thanks to @andranik3949 for pointing out this missing test
registry.view<const int>().each([](const auto entity, const auto &value) {
ASSERT_EQ(entt::to_integral(entity), value);
ASSERT_EQ(static_cast<int>(entt::to_integral(entity)), value);
});
}
@ -2021,7 +2158,7 @@ TEST(Registry, RuntimePools) {
const auto entity = registry.create();
static_assert(std::is_same_v<decltype(registry.storage<empty_type>()), entt::storage_type_t<empty_type> &>);
static_assert(std::is_same_v<decltype(std::as_const(registry).storage<empty_type>()), const entt::storage_type_t<empty_type> &>);
static_assert(std::is_same_v<decltype(std::as_const(registry).storage<empty_type>()), const entt::storage_type_t<empty_type> *>);
static_assert(std::is_same_v<decltype(registry.storage("other"_hs)), entt::storage_type_t<empty_type>::base_type *>);
static_assert(std::is_same_v<decltype(std::as_const(registry).storage("other"_hs)), const entt::storage_type_t<empty_type>::base_type *>);
@ -2030,7 +2167,7 @@ TEST(Registry, RuntimePools) {
ASSERT_EQ(std::as_const(registry).storage("rehto"_hs), nullptr);
ASSERT_EQ(&registry.storage<empty_type>("other"_hs), &storage);
ASSERT_NE(&std::as_const(registry).storage<empty_type>(), &storage);
ASSERT_NE(std::as_const(registry).storage<empty_type>(), &storage);
ASSERT_FALSE(registry.any_of<empty_type>(entity));
ASSERT_FALSE(storage.contains(entity));
@ -2070,6 +2207,7 @@ TEST(Registry, Storage) {
entt::registry registry;
const auto entity = registry.create();
auto &storage = registry.storage<int>("int"_hs);
storage.emplace(entity);
@ -2149,8 +2287,7 @@ TEST(Registry, RegistryStorageIterator) {
TEST(Registry, RegistryStorageIteratorConversion) {
entt::registry registry;
const auto entity = registry.create();
registry.emplace<int>(entity);
registry.storage<int>();
auto proxy = registry.storage();
auto cproxy = std::as_const(registry).storage();
@ -2176,6 +2313,22 @@ TEST(Registry, RegistryStorageIteratorConversion) {
ASSERT_NE(++cit, it);
}
TEST(Registry, VoidType) {
using namespace entt::literals;
entt::registry registry;
const auto entity = registry.create();
auto &storage = registry.storage<void>("custom"_hs);
storage.emplace(entity);
ASSERT_TRUE(registry.storage<void>().empty());
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>());
}
TEST(Registry, NoEtoType) {
entt::registry registry;
const auto entity = registry.create();

View File

@ -1,5 +1,6 @@
#include <iterator>
#include <gtest/gtest.h>
#include <entt/entity/mixin.hpp>
#include <entt/entity/registry.hpp>
#include <entt/entity/storage.hpp>
#include "../common/throwing_allocator.hpp"
@ -30,9 +31,20 @@ void listener(counter &counter, Registry &, typename Registry::entity_type) {
++counter.value;
}
TEST(SighStorageMixin, GenericType) {
struct empty_each_tag final {};
template<>
struct entt::basic_storage<empty_each_tag, entt::entity, std::allocator<empty_each_tag>>: entt::basic_storage<void, entt::entity, std::allocator<void>> {
basic_storage(const std::allocator<empty_each_tag> &) {}
[[nodiscard]] iterable each() noexcept {
return {internal::extended_storage_iterator{base_type::end()}, internal::extended_storage_iterator{base_type::end()}};
}
};
TEST(SighMixin, GenericType) {
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
entt::sigh_storage_mixin<entt::storage<int>> pool;
entt::sigh_mixin<entt::storage<int>> pool;
entt::sparse_set &base = pool;
entt::registry registry;
@ -40,10 +52,21 @@ TEST(SighStorageMixin, GenericType) {
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
ASSERT_TRUE(pool.empty());
pool.insert(entities, entities + 1u);
pool.erase(entities[0u]);
ASSERT_TRUE(pool.empty());
ASSERT_EQ(on_construct.value, 0);
ASSERT_EQ(on_destroy.value, 0);
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
ASSERT_NE(base.emplace(entities[0u]), base.end());
ASSERT_NE(base.push(entities[0u]), base.end());
pool.emplace(entities[1u]);
@ -61,7 +84,7 @@ TEST(SighStorageMixin, GenericType) {
ASSERT_EQ(on_destroy.value, 2);
ASSERT_TRUE(pool.empty());
ASSERT_NE(base.insert(std::begin(entities), std::end(entities)), base.end());
ASSERT_NE(base.push(std::begin(entities), std::end(entities)), base.end());
ASSERT_EQ(pool.get(entities[0u]), 0);
ASSERT_EQ(pool.get(entities[1u]), 0);
@ -95,9 +118,9 @@ TEST(SighStorageMixin, GenericType) {
ASSERT_TRUE(pool.empty());
}
TEST(SighStorageMixin, StableType) {
TEST(SighMixin, StableType) {
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
entt::sigh_storage_mixin<entt::storage<stable_type>> pool;
entt::sigh_mixin<entt::storage<stable_type>> pool;
entt::sparse_set &base = pool;
entt::registry registry;
@ -108,7 +131,7 @@ TEST(SighStorageMixin, StableType) {
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
ASSERT_NE(base.emplace(entities[0u]), base.end());
ASSERT_NE(base.push(entities[0u]), base.end());
pool.emplace(entities[1u]);
@ -126,7 +149,7 @@ TEST(SighStorageMixin, StableType) {
ASSERT_EQ(on_destroy.value, 2);
ASSERT_FALSE(pool.empty());
ASSERT_NE(base.insert(std::begin(entities), std::end(entities)), base.end());
ASSERT_NE(base.push(std::begin(entities), std::end(entities)), base.end());
ASSERT_EQ(pool.get(entities[0u]).value, 0);
ASSERT_EQ(pool.get(entities[1u]).value, 0);
@ -160,9 +183,9 @@ TEST(SighStorageMixin, StableType) {
ASSERT_FALSE(pool.empty());
}
TEST(SighStorageMixin, EmptyType) {
TEST(SighMixin, EmptyType) {
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
entt::sigh_storage_mixin<entt::storage<empty_type>> pool;
entt::sigh_mixin<entt::storage<empty_type>> pool;
entt::sparse_set &base = pool;
entt::registry registry;
@ -173,7 +196,7 @@ TEST(SighStorageMixin, EmptyType) {
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
ASSERT_NE(base.emplace(entities[0u]), base.end());
ASSERT_NE(base.push(entities[0u]), base.end());
pool.emplace(entities[1u]);
@ -191,7 +214,7 @@ TEST(SighStorageMixin, EmptyType) {
ASSERT_EQ(on_destroy.value, 2);
ASSERT_TRUE(pool.empty());
ASSERT_NE(base.insert(std::begin(entities), std::end(entities)), base.end());
ASSERT_NE(base.push(std::begin(entities), std::end(entities)), base.end());
ASSERT_TRUE(pool.contains(entities[0u]));
ASSERT_TRUE(pool.contains(entities[1u]));
@ -225,9 +248,9 @@ TEST(SighStorageMixin, EmptyType) {
ASSERT_TRUE(pool.empty());
}
TEST(SighStorageMixin, NonDefaultConstructibleType) {
TEST(SighMixin, NonDefaultConstructibleType) {
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
entt::sigh_storage_mixin<entt::storage<non_default_constructible>> pool;
entt::sigh_mixin<entt::storage<non_default_constructible>> pool;
entt::sparse_set &base = pool;
entt::registry registry;
@ -238,12 +261,12 @@ TEST(SighStorageMixin, NonDefaultConstructibleType) {
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
ASSERT_EQ(base.emplace(entities[0u]), base.end());
ASSERT_EQ(base.push(entities[0u]), base.end());
pool.emplace(entities[1u], 3);
ASSERT_EQ(pool.size(), 1u);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_construct.value, 1);
ASSERT_EQ(on_destroy.value, 0);
ASSERT_FALSE(pool.empty());
@ -253,11 +276,11 @@ TEST(SighStorageMixin, NonDefaultConstructibleType) {
base.erase(entities[1u]);
ASSERT_EQ(pool.size(), 0u);
ASSERT_EQ(on_construct.value, 2);
ASSERT_EQ(on_construct.value, 1);
ASSERT_EQ(on_destroy.value, 1);
ASSERT_TRUE(pool.empty());
ASSERT_EQ(base.insert(std::begin(entities), std::end(entities)), base.end());
ASSERT_EQ(base.push(std::begin(entities), std::end(entities)), base.end());
ASSERT_FALSE(pool.contains(entities[0u]));
ASSERT_FALSE(pool.contains(entities[1u]));
@ -266,7 +289,7 @@ TEST(SighStorageMixin, NonDefaultConstructibleType) {
pool.insert(std::begin(entities), std::end(entities), 3);
ASSERT_EQ(pool.size(), 2u);
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_construct.value, 3);
ASSERT_EQ(on_destroy.value, 1);
ASSERT_FALSE(pool.empty());
@ -276,13 +299,13 @@ TEST(SighStorageMixin, NonDefaultConstructibleType) {
pool.erase(std::begin(entities), std::end(entities));
ASSERT_EQ(pool.size(), 0u);
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_construct.value, 3);
ASSERT_EQ(on_destroy.value, 3);
ASSERT_TRUE(pool.empty());
}
TEST(SighStorageMixin, VoidType) {
entt::sigh_storage_mixin<entt::storage<void>> pool;
TEST(SighMixin, VoidType) {
entt::sigh_mixin<entt::storage<void>> pool;
entt::registry registry;
counter on_construct{};
@ -297,7 +320,7 @@ TEST(SighStorageMixin, VoidType) {
ASSERT_EQ(pool.type(), entt::type_id<void>());
ASSERT_TRUE(pool.contains(entt::entity{99}));
entt::sigh_storage_mixin<entt::storage<void>> other{std::move(pool)};
entt::sigh_mixin<entt::storage<void>> other{std::move(pool)};
ASSERT_FALSE(pool.contains(entt::entity{99}));
ASSERT_TRUE(other.contains(entt::entity{99}));
@ -313,8 +336,8 @@ TEST(SighStorageMixin, VoidType) {
ASSERT_EQ(on_destroy.value, 1);
}
TEST(SighStorageMixin, Move) {
entt::sigh_storage_mixin<entt::storage<int>> pool;
TEST(SighMixin, Move) {
entt::sigh_mixin<entt::storage<int>> pool;
entt::registry registry;
counter on_construct{};
@ -330,7 +353,7 @@ TEST(SighStorageMixin, Move) {
ASSERT_TRUE(std::is_move_assignable_v<decltype(pool)>);
ASSERT_EQ(pool.type(), entt::type_id<int>());
entt::sigh_storage_mixin<entt::storage<int>> other{std::move(pool)};
entt::sigh_mixin<entt::storage<int>> other{std::move(pool)};
ASSERT_TRUE(pool.empty());
ASSERT_FALSE(other.empty());
@ -347,7 +370,7 @@ TEST(SighStorageMixin, Move) {
ASSERT_EQ(pool.get(entt::entity{3}), 3);
ASSERT_EQ(other.at(0u), static_cast<entt::entity>(entt::null));
other = entt::sigh_storage_mixin<entt::storage<int>>{};
other = entt::sigh_mixin<entt::storage<int>>{};
other.bind(entt::forward_as_any(registry));
other.emplace(entt::entity{42}, 42);
@ -365,9 +388,9 @@ TEST(SighStorageMixin, Move) {
ASSERT_EQ(on_destroy.value, 1);
}
TEST(SighStorageMixin, Swap) {
entt::sigh_storage_mixin<entt::storage<int>> pool;
entt::sigh_storage_mixin<entt::storage<int>> other;
TEST(SighMixin, Swap) {
entt::sigh_mixin<entt::storage<int>> pool;
entt::sigh_mixin<entt::storage<int>> other;
entt::registry registry;
counter on_construct{};
@ -411,7 +434,100 @@ TEST(SighStorageMixin, Swap) {
ASSERT_EQ(on_destroy.value, 3);
}
TEST(SighStorageMixin, CustomAllocator) {
TEST(SighMixin, EmptyEachStorage) {
entt::sigh_mixin<entt::storage<empty_each_tag>> pool;
entt::registry registry;
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
ASSERT_TRUE(pool.empty());
ASSERT_EQ(on_destroy.value, 0);
pool.push(entt::entity{42});
ASSERT_FALSE(pool.empty());
ASSERT_EQ(on_destroy.value, 0);
ASSERT_NE(pool.begin(), pool.end());
ASSERT_EQ(pool.each().begin(), pool.each().end());
ASSERT_EQ(on_destroy.value, 0);
pool.clear();
ASSERT_EQ(pool.begin(), pool.end());
ASSERT_EQ(pool.each().begin(), pool.each().end());
// no signal at all because of the (fake) empty iterable
ASSERT_EQ(on_destroy.value, 0);
}
TEST(SighMixin, StorageEntity) {
using traits_type = entt::entt_traits<entt::entity>;
entt::sigh_mixin<entt::storage<entt::entity>> pool;
entt::registry registry;
counter on_construct{};
counter on_destroy{};
pool.bind(entt::forward_as_any(registry));
pool.on_construct().connect<&listener<entt::registry>>(on_construct);
pool.on_destroy().connect<&listener<entt::registry>>(on_destroy);
pool.push(entt::entity{1});
ASSERT_EQ(on_construct.value, 1);
ASSERT_EQ(on_destroy.value, 0);
ASSERT_EQ(pool.size(), 2u);
ASSERT_EQ(pool.in_use(), 1u);
pool.erase(entt::entity{1});
ASSERT_EQ(on_construct.value, 1);
ASSERT_EQ(on_destroy.value, 1);
ASSERT_EQ(pool.size(), 2u);
ASSERT_EQ(pool.in_use(), 0u);
pool.push(traits_type::construct(0, 2));
pool.push(traits_type::construct(2, 1));
ASSERT_TRUE(pool.contains(traits_type::construct(0, 2)));
ASSERT_TRUE(pool.contains(traits_type::construct(1, 1)));
ASSERT_TRUE(pool.contains(traits_type::construct(2, 1)));
ASSERT_EQ(on_construct.value, 3);
ASSERT_EQ(on_destroy.value, 1);
ASSERT_EQ(pool.size(), 3u);
ASSERT_EQ(pool.in_use(), 2u);
pool.clear();
ASSERT_EQ(pool.size(), 0u);
ASSERT_EQ(pool.in_use(), 0u);
ASSERT_EQ(on_construct.value, 3);
ASSERT_EQ(on_destroy.value, 3);
pool.emplace();
pool.emplace(entt::entity{0});
entt::entity entities[1u]{};
pool.insert(entities, entities + 1u);
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_destroy.value, 3);
ASSERT_EQ(pool.size(), 3u);
ASSERT_EQ(pool.in_use(), 3u);
pool.clear();
ASSERT_EQ(pool.size(), 0u);
ASSERT_EQ(pool.in_use(), 0u);
}
TEST(SighMixin, CustomAllocator) {
auto test = [](auto pool, auto alloc) {
using registry_type = typename decltype(pool)::registry_type;
registry_type registry;
@ -466,12 +582,12 @@ TEST(SighStorageMixin, CustomAllocator) {
test::throwing_allocator<entt::entity> allocator{};
test(entt::sigh_storage_mixin<entt::basic_storage<int, entt::entity, test::throwing_allocator<int>>>{allocator}, allocator);
test(entt::sigh_storage_mixin<entt::basic_storage<std::true_type, entt::entity, test::throwing_allocator<std::true_type>>>{allocator}, allocator);
test(entt::sigh_storage_mixin<entt::basic_storage<stable_type, entt::entity, test::throwing_allocator<stable_type>>>{allocator}, allocator);
test(entt::sigh_mixin<entt::basic_storage<int, entt::entity, test::throwing_allocator<int>>>{allocator}, allocator);
test(entt::sigh_mixin<entt::basic_storage<std::true_type, entt::entity, test::throwing_allocator<std::true_type>>>{allocator}, allocator);
test(entt::sigh_mixin<entt::basic_storage<stable_type, entt::entity, test::throwing_allocator<stable_type>>>{allocator}, allocator);
}
TEST(SighStorageMixin, ThrowingAllocator) {
TEST(SighMixin, ThrowingAllocator) {
auto test = [](auto pool) {
using pool_allocator_type = typename decltype(pool)::allocator_type;
using value_type = typename decltype(pool)::value_type;
@ -511,7 +627,7 @@ TEST(SighStorageMixin, ThrowingAllocator) {
test::throwing_allocator<entt::entity>::trigger_on_allocate = true;
ASSERT_THROW(base.emplace(entt::entity{0}), test::throwing_allocator<entt::entity>::exception_type);
ASSERT_THROW(base.push(entt::entity{0}), test::throwing_allocator<entt::entity>::exception_type);
ASSERT_FALSE(base.contains(entt::entity{0}));
ASSERT_TRUE(base.empty());
@ -543,12 +659,12 @@ TEST(SighStorageMixin, ThrowingAllocator) {
ASSERT_EQ(on_destroy.value, 1);
};
test(entt::sigh_storage_mixin<entt::basic_storage<int, entt::entity, test::throwing_allocator<int>>>{});
test(entt::sigh_storage_mixin<entt::basic_storage<stable_type, entt::entity, test::throwing_allocator<stable_type>>>{});
test(entt::sigh_mixin<entt::basic_storage<int, entt::entity, test::throwing_allocator<int>>>{});
test(entt::sigh_mixin<entt::basic_storage<stable_type, entt::entity, test::throwing_allocator<stable_type>>>{});
}
TEST(SighStorageMixin, ThrowingComponent) {
entt::sigh_storage_mixin<entt::storage<test::throwing_type>> pool;
TEST(SighMixin, ThrowingComponent) {
entt::sigh_mixin<entt::storage<test::throwing_type>> pool;
using registry_type = typename decltype(pool)::registry_type;
registry_type registry;

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More