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
35 KiB
Crash Course: core functionalities
Table of Contents
- Introduction
- Any as in any type
- Compressed pair
- Enum as bitmask
- Hashed strings
- Iterators
- Memory
- Monostate
- Type support
- Unique sequential identifiers
- Utilities
Introduction
EnTT
comes with a bunch of core functionalities mostly used by the other parts
of the library.
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
offers its own any
type. It may seem redundant considering that C++17
introduced std::any
, but it is not (hopefully).
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 bind it to the type
system of the library and therefore with its integrated RTTI support.
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.
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:
// an empty container
entt::any empty{};
// a container for an int
entt::any any{0};
// in place construction
entt::any in_place{std::in_place_type<int>, 42};
Alternatively, the make_any
function serves the same purpose but requires to
always be explicit about the type:
entt::any any = entt::make_any<int>(42);
In both cases, the any
class takes the burden of destroying the contained
element when required, regardless of the storage strategy used for the specific
object.
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 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:
entt::any any{42};
entt::any value{3};
// assigns by copy
any.assign(value);
// assigns by move
any.assign(std::move(value));
The any
class performs a check on the type information and whether or not the
original type was copy or move assignable, as appropriate.
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
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:
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.
Refer to the EnTT
type system documentation for more details about how
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:
int value = 42;
entt::any any{std::in_place_type<int &>(value)};
entt::any cany = entt::make_any<const int &>(value);
entt::any fwd = entt::forward_as_any(value);
any.emplace<const int &>(value);
In other words, whenever any
is explicitly told to construct an alias, it
acts as a pointer to the original instance rather than making a copy of it or
moving it internally. The contained object is never destroyed and users must
ensure that its lifetime exceeds that of the container.
Similarly, it's possible to create non-owning copies of any
from an existing
object:
// aliasing constructor
entt::any ref = other.as_ref();
In this case, it doesn't matter if the original container actually holds an 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.
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.
In particular, the data
member function invoked on a non-const instance of
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.
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.
Small buffer optimization
The any
class uses a technique called small buffer optimization to reduce
the number of allocations where possible.
The default reserved size for an instance of any
is sizeof(double[2])
.
However, this is also configurable if needed. In fact, any
is defined as an
alias for basic_any<Len>
, where Len
is the size above.
Users can easily set a custom size or define their own aliases:
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.
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.
It's provided as an optional second parameter following the desired size for the
internal storage:
using my_any = entt::basic_any<sizeof(double[4]), alignof(double[4])>;
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
Primarily designed for internal use and far from being feature complete, the
compressed_pair
class does exactly what it promises: it tries to reduce the
size of a pair by exploiting Empty Base Class Optimization (or EBCO).
This class is not a drop-in replacement for std::pair
. However, it offers
enough functionalities to be a good alternative for when reducing memory usage
is more important than having some cool and probably useless feature.
Although the API is very close to that of std::pair
(apart from the fact that
the template parameters are inferred from the constructor and therefore there is
no entt::make_compressed_pair
), the major difference is that first
and
second
are functions for implementation requirements:
entt::compressed_pair pair{0, 3.};
pair.first() = 42;
There isn't much to describe then. It's recommended to rely on documentation and intuition. At the end of the day, it's just a pair and nothing more.
Enum as bitmask
Sometimes it's useful to be able to use enums as bitmasks. However, enum classes
aren't really suitable for the purpose. Main problem is that they don't convert
implicitly to their underlying type.
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 bitmasks transparently.
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):
enum class my_flag {
unknown = 0x01,
enabled = 0x02,
disabled = 0x04
};
const my_flag flags = my_flag::enabled;
const bool is_enabled = !!(flags & my_flag::enabled);
The problem with adding all operators to the global scope is that these come
into play even when not required, with the risk of introducing errors that are
difficult to deal with.
However, C++ offers enough tools to get around this problem. In particular, the
library requires users to register the enum classes for which bitmask support
should be enabled:
template<>
struct entt::enum_as_bitmask<my_flag>
: std::true_type
{};
This is handy when dealing with enum classes defined by third party libraries and over which the user has no control. However, it's also verbose and can be avoided by adding a specific value to the enum class itself:
enum class my_flag {
unknown = 0x01,
enabled = 0x02,
disabled = 0x04,
_entt_enum_as_bitmask
};
In this case, there is no need to specialize the enum_as_bitmask
traits, since
EnTT
automatically detects the flag and enables the bitmask support.
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
Hashed strings are human-readable identifiers in the codebase that turn into
numeric values at runtime, thus without affecting performance.
The class has an implicit constexpr
constructor that chews a bunch of
characters. Once created, one can get the original string by means of the data
member function or convert the instance into a number.
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:
auto load(entt::hashed_string::hash_type resource) {
// uses the numeric representation of the resource to load and return it
}
auto resource = load(entt::hashed_string{"gui/background"});
There is also a user defined literal dedicated to hashed strings to make them more user-friendly:
using namespace entt::literals;
constexpr auto str = "text"_hs;
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
.
The class also offers the necessary functionalities to create hashed strings at
runtime:
std::string orig{"text"};
// create a full-featured hashed string...
entt::hashed_string str{orig.c_str()};
// ... or compute only the unique identifier
const auto hash = entt::hashed_string::value(orig.c_str());
This possibility shouldn't be exploited in tight loops, since the computation takes place at runtime and no longer at compile-time. It could therefore affect performance to some degrees.
Wide characters
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>
.
In this case, the user defined literal to use to create hashed strings on the
fly is _hws
:
constexpr auto str = L"text"_hws;
The hash type of hashed_wstring
is the same as its counterpart.
Conflicts
The hashed string class uses FNV-1a internally to hash strings. Because of the
pigeonhole principle, conflicts are possible. This is a fact.
There is no silver bullet to solve the problem of conflicts when dealing with
hashing functions. In this case, the best solution is likely to give up. That's
all.
After all, human-readable unique identifiers aren't something strictly defined
and over which users have not the control. Choosing a slightly different
identifier is probably the best solution to make the conflict disappear in this
case.
Iterators
Writing and working with iterators isn't always easy. More often than not it
also leads to duplicated code.
EnTT
tries to overcome this problem by offering some utilities designed to
make this hard work easier.
Input iterator pointer
When writing an input iterator that returns in-place constructed values if
dereferenced, it's not always straightforward to figure out what value_type
is
and how to make it behave like a full-fledged pointer.
Conversely, it would be very useful to have an operator->
available on the
iterator itself that always works without too much complexity.
The input iterator pointer is meant for this. It's a small class that wraps the in-place constructed value and adds some functions on top of it to make it suitable for use with input iterators:
struct iterator_type {
using value_type = std::pair<first_type, second_type>;
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
// ...
}
The library makes extensive use of this class internally. In many cases, the
value_type
of the returned iterators is just an input iterator pointer.
Iota iterator
Waiting for C++20, this iterator accepts an integral value and returns all elements in a certain range:
entt::iota_iterator first{0};
entt::iota_iterator last{100};
for(; first != last; ++first) {
int value = *first;
// ...
}
In the future, views will replace this class. Meanwhile, the library makes some interesting uses of it when a range of integral values is to be returned to the user.
Iterable adaptor
Typically, a container class provides begin
and end
member functions (with
their const counterparts) for iteration.
However, it can happen that a class offers multiple iteration methods or allows
users to iterate different sets of elements.
The iterable adaptor is a utility class that makes it easier to use and access
data in this case.
It accepts a couple of iterators (or an iterator and a sentinel) and offers an
iterable object with all the expected methods like begin
, end
and whatnot.
The library uses this class extensively.
Think for example of views, which can be iterated to access entities but also
offer a method of obtaining an iterable object that returns tuples of entities
and components at once.
Another example is the registry class which allows users to iterate its storage
by returning an iterable object for the purpose.
Memory
There are a handful of tools within EnTT
to interact with memory in one way or
another.
Some are geared towards simplifying the implementation of (internal or external)
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
acronyms like POCCA, POCMA or POCS.
I won't describe them here in detail. Instead, I recommend reading the inline
documentation to those interested in the subject.
Power of two and fast modulus
Finding out if a number is a power of two (is_power_of_two
) or what the next
power of two is given a random value (next_power_of_two
) is very useful at
times.
For example, it helps to allocate memory in pages having a size suitable for the
fast modulus:
const std::size_t result = entt::fast_mod(value, modulus);
Where modulus
is necessarily a power of two. Perhaps not everyone knows that
this type of operation is far superior in terms of performance to the basic
modulus and for this reason preferred in many areas.
Allocator aware unique pointers
A nasty thing in C++ (at least up to C++20) is the fact that shared pointers
support allocators while unique pointers don't.
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
necessity:
std::unique_ptr<my_type, entt::allocation_deleter<my_type>> ptr = entt::allocate_unique<my_type>(allocator, arguments);
Although the internal implementation is slightly different from what is proposed for the standard, this function offers an API that is a drop-in replacement for 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).
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.
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:
entt::monostate<entt::hashed_string{"mykey"}>{} = true;
entt::monostate<"mykey"_hs>{} = 42;
// ...
const bool b = entt::monostate<"mykey"_hs>{};
const int i = entt::monostate<entt::hashed_string{"mykey"}>{};
Type support
EnTT
provides some basic information about types of all kinds.
It also offers additional features that are not yet available in the standard
library or that will never be.
Built-in RTTI support
Runtime type identification support (or RTTI) is one of the most frequently
disabled features in the C++ world, especially in the gaming sector. Regardless
of the reasons for this, it's often a shame not to be able to rely on opaque
type information at runtime.
The library tries to fill this gap by offering a built-in system that doesn't
serve as a replacement but comes very close to being one and offers similar
information to that provided by its counterpart.
Basically, the whole system relies on a handful of classes. In particular:
-
The unique sequential identifier associated with a given type:
auto index = entt::type_index<a_type>::value();
The returned value isn't guaranteed to be stable across different runs.
However, it can be very useful as index in associative and unordered associative containers or for positional accesses in a vector or an array.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:template<typename Type> struct entt::type_index<Type, std::void_d<decltype(Type::index())>> { static entt::id_type value() noexcept { return Type::index(); } };
Indexes must be sequentially generated in this case.
The tool is widely used withinEnTT
. Generating indices not sequentially would break an assumption and would likely lead to undesired behaviors. -
The hash value associated with a given type:
auto hash = entt::type_hash<a_type>::value();
In general, the
value
function exposed bytype_hash
is alsoconstexpr
but this isn't guaranteed for all compilers and platforms (although it's valid with the most well-known and popular ones).This function can use non-standard features of the language for its own purposes. This makes it possible to provide compile-time identifiers that remain stable across different runs.
Users can prevent the library from using these features by means of theENTT_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 it happens with
type_index
, alsotype_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:
auto name = entt::type_name<a_type>::value();
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.
For example, given the following class:struct my_type { /* ... */ };
The name is
my_type
when compiled with GCC or CLang andstruct my_type
when MSVC is in use.
Most of the time the name is also retrieved at compile-time and is therefore always returned through anstd::string_view
. Users can easily access it and modify it as needed, for example by removing the wordstruct
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 these features by means of the
ENTT_STANDARD_CPP
definition. In this case, the name is just empty.As it happens with
type_index
, alsotype_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 made available by the standard library.
Type info
The type_info
class isn't a drop-in replacement for std::type_info
but can
provide similar information which are not implementation defined and don't
require to enable RTTI.
Therefore, they can sometimes be even more reliable than those obtained
otherwise.
Its type defines an opaque class that is also copyable and movable.
Objects of this type are generally returned by the type_id
functions:
// by type
auto info = entt::type_id<a_type>();
// by value
auto other = entt::type_id(42);
All elements thus received are nothing more than const references to instances
of type_info
with static storage duration.
This is convenient for saving the entire object aside for the cost of a pointer.
However, nothing prevents from constructing type_info
objects directly:
entt::type_info info{std::in_place_type<int>};
These are the information made available by type_info
:
-
The index associated with a given type:
auto idx = entt::type_id<a_type>().index();
This is also an alias for the following:
auto idx = entt::type_index<std::remove_cv_t<std::remove_reference_t<a_type>>>::value();
-
The hash value associated with a given type:
auto hash = entt::type_id<a_type>().hash();
This is also an alias for the following:
auto hash = entt::type_hash<std::remove_cv_t<std::remove_reference_t<a_type>>>::value();
-
The name associated with a given type:
auto name = entt::type_id<my_type>().name();
This is also an alias for the following:
auto name = entt::type_name<std::remove_cv_t<std::remove_reference_t<a_type>>>::value();
Where all accessed features are available at compile-time, the type_info
class
is also fully constexpr
. However, this cannot be guaranteed in advance and
depends mainly on the compiler in use and any specializations of the classes
described above.
Almost unique identifiers
Since the default non-standard, compile-time implementation of type_hash
makes
use of hashed strings, it may happen that two types are assigned the same hash
value.
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, type_name
returns the same
value for the two types.
Fortunately, there are several easy ways to deal with this:
-
The most trivial one is to define the
ENTT_STANDARD_CPP
macro. Runtime identifiers don't suffer from the same problem in fact. However, this solution doesn't work well with a plugin system, where the libraries aren't linked. -
Another possibility is to specialize the
type_name
class for one of the conflicting types, in order to assign it a custom identifier. This is probably the easiest solution that also preserves the feature of the tool. -
A fully customized identifier generation policy (based for example on enum classes or preprocessing steps) may represent yet another option.
These are just some examples of possible approaches to the problem but there are
many others. As already mentioned above, since users have full control over
their types, this problem is in any case easy to solve and should not worry too
much.
In all likelihood, it will never happen to run into a conflict anyway.
Type traits
A handful of utilities and traits not present in the standard template library
but which can be useful in everyday life.
This list is not exhaustive and contains only some of the most useful
classes. Refer to the inline documentation for more information on the features
offered by this module.
Size of
The standard operator sizeof
complains 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.
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:
const auto size = entt::size_of_v<void>;
Is applicable
The standard library offers the great std::is_invocable
trait in several
forms. This takes a function type and a series of arguments and returns true if
the condition is satisfied.
Moreover, users are also provided with std::apply
, a tool for combining
invocable elements and tuples of arguments.
It would therefore be a good idea to have a variant of std::is_invocable
that
also accepts its arguments in the form of a tuple-like type, so as to complete
the offer:
constexpr bool result = entt::is_applicable<Func, std::tuple<a_type, another_type>>;
This trait is built on top of std::is_invocable
and does nothing but unpack a
tuple-like type and simplify the code at the call site.
Constness as
A utility to easily transfer the constness of a type to another type:
// type is const dst_type because of the constness of src_type
using type = entt::constness_as_t<dst_type, const src_type>;
The trait is subject to the rules of the language. For example, transferring constness between references won't give the desired effect.
Member class type
The auto
template parameter introduced with C++17 made it possible to simplify
many class templates and template functions but also made the class type opaque
when members are passed as template arguments.
The purpose of this utility is to extract the class type in a few lines of code:
template<typename Member>
using clazz = entt::member_class_t<Member>;
N-th argument
A utility to quickly find the n-th argument of a function, member function or data member (for blind operations on opaque types):
using type = entt::nth_argument_t<1u, &clazz::member>;
Disambiguation of overloaded functions is the responsibility of the user, should it be needed.
Integral constant
Since std::integral_constant
may be annoying because of its form that requires
to specify both a type and a value of that type, there is a more user-friendly
shortcut for the creation of integral constants.
This shortcut is the alias template entt::integral_constant
:
constexpr auto constant = entt::integral_constant<42>;
Among the other uses, when combined with a hashed string it helps to define tags as human-readable names where actual types would be required otherwise:
constexpr auto enemy_tag = entt::integral_constant<"enemy"_hs>;
registry.emplace<enemy_tag>(entity);
Tag
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.
This shortcut is the alias template entt::tag
.
If used in combination with hashed strings, it helps to use human-readable names where types would be required otherwise. As an example:
registry.emplace<entt::tag<"enemy"_hs>>(entity);
However, this isn't the only permitted use. Literally any value convertible to
id_type
is a good candidate, such as the named constants of an unscoped enum.
Type list and value list
There is no respectable library where the much desired type list can be
missing.
EnTT
is no exception and provides (making extensive use of it internally) the
type_list
type, in addition to its value_list
counterpart dedicated to
non-type template parameters.
Here is a (possibly incomplete) list of the functionalities that come with a type list:
type_list_element[_t]
to get the N-th element of a type list.type_list_index[_v]
to get the index of a given element of a type list.type_list_cat[_t]
and a handyoperator+
to concatenate type lists.type_list_unique[_t]
to remove duplicate types from a type list.type_list_contains[_v]
to know if a type list contains a given type.type_list_diff[_t]
to remove types from type lists.type_list_transform[_t]
to transform a range and create another type list.
I'm also pretty sure that more and more utilities will be added over time as
needs become apparent.
Many of these functionalities also exist in their version dedicated to value
lists. We therefore have value_list_element[_v]
as well as
value_list_cat[_t]
and so on.
Unique sequential identifiers
Sometimes it's useful to be able to give unique, sequential numeric identifiers
to types either at compile-time or runtime.
There are plenty of different solutions for this out there and I could have used
one of them. However, I decided to spend my time to define a couple of tools
that fully embraces what the modern C++ has to offer.
Compile-time generator
To generate sequential numeric identifiers at compile-time, EnTT
offers the
ident
class template:
// defines the identifiers for the given types
using id = entt::ident<a_type, another_type>;
// ...
switch(a_type_identifier) {
case id::value<a_type>:
// ...
break;
case id::value<another_type>:
// ...
break;
default:
// ...
}
This is what this class template has to offer: a value
inline variable that
contains a numeric identifier for the given type. It can be used in any context
where constant expressions are required.
As long as the list remains unchanged, identifiers are also guaranteed to be stable across different runs. In case they have been used in a production environment and a type has to be removed, one can just use a placeholder to leave the other identifiers unchanged:
template<typename> struct ignore_type {};
using id = entt::ident<
a_type_still_valid,
ignore_type<no_longer_valid_type>,
another_type_still_valid
>;
Perhaps a bit ugly to see in a codebase but it gets the job done at least.
Runtime generator
The family
class template helps to generate sequential numeric identifiers for
types at runtime:
// defines a custom generator
using id = entt::family<struct my_tag>;
// ...
const auto a_type_id = id::value<a_type>;
const auto another_type_id = id::value<another_type>;
This is what a family has to offer: a value
inline variable that contains a
numeric identifier for the given type.
The generator is customizable, so as to get different sequences for different
purposes if needed.
Identifiers aren't guaranteed to be stable across different runs. Indeed it mostly depends on the flow of execution.
Utilities
It's not possible to escape the temptation to add utilities of some kind to a
library. In fact, EnTT
also provides a handful of tools to simplify the
life of developers:
-
entt::identity
: the identity function object that will be available with C++20. It returns its argument unchanged and nothing more. It's useful as a sort of do nothing function in template programming. -
entt::overload
: a tool to disambiguate different overloads from their function type. It works with both free and member functions.
Consider the following definition:struct clazz { void bar(int) {} void bar() {} };
This utility can be used to get the right overload as:
auto *member = entt::overload<void(int)>(&clazz::bar);
The line above is literally equivalent to:
auto *member = static_cast<void(clazz:: *)(int)>(&clazz::bar);
Just easier to read and shorter to type.
-
entt::overloaded
: a small class template used to create a new type with an overloadedoperator()
from a bunch of lambdas or functors.
As an example:entt::overloaded func{ [](int value) { /* ... */ }, [](char value) { /* ... */ } }; func(42); func('c');
Rather useful when doing metaprogramming and having to pass to a function a callable object that supports multiple types at once.
-
entt::y_combinator
: this is a C++ implementation of the y-combinator. If it's not clear what it is, there is probably no need for this utility.
Below is a small example to show its use:entt::y_combinator gauss([](const auto &self, auto value) -> unsigned int { return value ? (value + self(value-1u)) : 0; }); const auto result = gauss(3u);
Maybe convoluted at a first glance but certainly effective. Unfortunately, the language doesn't make it possible to do much better.
This is a rundown of the (actually few) utilities made available by EnTT
. The
list will probably grow over time but the size of each will remain rather small,
as has been the case so far.