diff --git a/CMakeLists.txt b/CMakeLists.txt index b130c84102eb591a7ff16272863cfd837a91ea50..c01b1c12e8e42228b4429679218b776b4011fa06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,7 @@ add_subdirectory( vendor/header_only ) # 3rd party integrations add_subdirectory( integrations/bullet ) +add_subdirectory( integrations/entt ) ## G++ enable insane checks diff --git a/demo/data/rollball.yml b/demo/data/rollball.yml index c1258bf2c631b5bb898b0053a62d484f25ecf40b..d31c9660c236e39cb971dd8e663451388227c379 100644 --- a/demo/data/rollball.yml +++ b/demo/data/rollball.yml @@ -8,11 +8,16 @@ prefabs: shape: type: box scale: [1.0, 5.0, 41] - phys::Body: - type: static - shape: - type: box - extents: [0.5, 2.5, 20.5] + gfx::material: + ambient: [0.0215, 0.1754, 0.0215] + diffuse: [1, 1, 1] + specular: [0.0633, 0.727811, 0.633] + shininess: 16 +# phys::Body: +# type: static +# shape: +# type: box +# extents: [0.5, 2.5, 20.5] # Wall Z shorter to avoid z-fighting - name: "wallZ" components: @@ -22,11 +27,16 @@ prefabs: shape: type: box scale: [39, 5, 1] - phys::Body: - type: static - shape: - type: box - extents: [ 19.5, 2.5, 0.5 ] + gfx::material: + ambient: [0.0215, 0.1754, 0.0215] + diffuse: [1, 1, 1] + specular: [0.0633, 0.727811, 0.633] + shininess: 16 +# phys::Body: +# type: static +# shape: +# type: box +# extents: [ 19.5, 2.5, 0.5 ] - name: "floor" components: Transform: @@ -35,11 +45,16 @@ prefabs: shape: type: box # we don't (currently) support planes... scale: [39, 0.5, 39] - phys::Body: - type: static - shape: - type: box # we don't (currently) support planes... - extents: [19.5, 0.25, 19.5] + gfx::material: + ambient: [0.0215, 0.1754, 0.0215] + diffuse: [1, 1, 1] + specular: [0.0633, 0.727811, 0.633] + shininess: 16 +# phys::Body: +# type: static +# shape: +# type: box # we don't (currently) support planes... +# extents: [19.5, 0.25, 19.5] - name: player components: Transform: @@ -52,10 +67,10 @@ prefabs: diffuse: [0.4, 0.4, 0.4] specular: [0.774597,0.774597,0.774597] shininess: 16 - phys::Body: - shape: - type: sphere - radius: 1 +# phys::Body: +# shape: +# type: sphere +# radius: 1 - name: collectable components: Transform: @@ -68,9 +83,9 @@ prefabs: diffuse: [1, 1, 1] specular: [0.0633, 0.727811, 0.633] shininess: 16 - phys::Body: - type: kinematic - shape: - type: box - phys::Callbacks: - phys::Cache: \ No newline at end of file +# phys::Body: +# type: kinematic +# shape: +# type: box +# phys::Callbacks: +# phys::Cache: \ No newline at end of file diff --git a/demo/demo/GameScene.cpp b/demo/demo/GameScene.cpp index 9fee528217c7ca5a62f316b6f9ad9c303b55d8df..165c4b99fa881fc35509e415d8fde2d99615efde 100644 --- a/demo/demo/GameScene.cpp +++ b/demo/demo/GameScene.cpp @@ -23,31 +23,31 @@ // #include "GameScene.h" +#include "fggl/entity/loader/loader.hpp" camera_type cam_mode = cam_free; -static void placeObject(fggl::ecs3::World& world, fggl::ecs::entity_t parent, fggl::ecs::entity_t prototype, glm::vec3 targetPos) { - auto obj = world.copy(prototype); - auto result = world.get<fggl::math::Transform>(obj); +static void placeObject(fggl::entity::EntityManager& world, fggl::entity::EntityID floor, fggl::entity::EntityFactory* factory, fggl::util::GUID prototype, glm::vec3 targetPos) { + auto obj = factory->create(prototype, world); + auto& result = world.get<fggl::math::Transform>(obj); int xPos = (int)targetPos.x; int zPos = (int)targetPos.z * -1; // figure out the floor height - auto heightMap = world.get<fggl::data::HeightMap>(parent); - targetPos.y = heightMap->getValue(xPos, zPos); // TODO should really be the gradient at the required point - - result->origin( targetPos ); + auto heightMap = world.get<fggl::data::HeightMap>(floor); + targetPos.y = heightMap.getValue(xPos, zPos); // TODO should really be the gradient at the required point + result.origin( targetPos ); } -static void process_camera(fggl::ecs3::World& ecs, const fggl::input::Input& input) { - auto cameras = ecs.findMatching<fggl::gfx::Camera>(); - fggl::ecs3::entity_t cam = cameras[0]; +static void process_camera(fggl::entity::EntityManager& ecs, const fggl::input::Input& input) { + auto cameras = ecs.find<fggl::gfx::Camera>(); + auto cam = cameras[0]; auto camTransform = ecs.get<fggl::math::Transform>(cam); auto camComp = ecs.get<fggl::gfx::Camera>(cam); - const glm::vec3 dir = ( camTransform->origin() - camComp->target ); + const glm::vec3 dir = ( camTransform.origin() - camComp.target ); const glm::vec3 forward = glm::normalize( dir ); // scroll wheel @@ -55,7 +55,7 @@ static void process_camera(fggl::ecs3::World& ecs, const fggl::input::Input& inp float delta = input.mouse.axis( fggl::input::MouseAxis::SCROLL_Y ); if ( (glm::length( dir ) < 25.0f && delta < 0.0f) || (glm::length( dir ) > 2.5f && delta > 0.0f) ) motion -= (forward * delta); - camTransform->origin( camTransform->origin() + motion ); + camTransform.origin( camTransform.origin() + motion ); if ( cam_mode == cam_arcball || input.mouse.down( fggl::input::MouseButton::MIDDLE ) ) { fggl::input::process_arcball(ecs, input, cam); @@ -65,35 +65,31 @@ static void process_camera(fggl::ecs3::World& ecs, const fggl::input::Input& inp fggl::input::process_edgescroll( ecs, input, cam ); } -static void setupCamera(fggl::ecs3::World& world) { - auto prototype = world.create(false); +static void setupCamera(fggl::entity::EntityManager& world) { + auto prototype = world.create(); // setup camera position/transform - auto* transform = world.add<fggl::math::Transform>(prototype); - if ( transform != nullptr) { - transform->origin(glm::vec3(10.0f, 3.0f, 10.0f)); - } + auto& transform = world.add<fggl::math::Transform>(prototype); + transform.origin(glm::vec3(10.0f, 3.0f, 10.0f)); // setup camera components world.add<fggl::gfx::Camera>(prototype); - auto* cameraKeys = world.add<fggl::input::FreeCamKeys>(prototype); - if ( cameraKeys != nullptr ) { - cameraKeys->forward = glfwGetKeyScancode(GLFW_KEY_W); - cameraKeys->backward = glfwGetKeyScancode(GLFW_KEY_S); - cameraKeys->left = glfwGetKeyScancode(GLFW_KEY_A); - cameraKeys->right = glfwGetKeyScancode(GLFW_KEY_D); - cameraKeys->rotate_cw = glfwGetKeyScancode(GLFW_KEY_Q); - cameraKeys->rotate_ccw = glfwGetKeyScancode(GLFW_KEY_E); - } + auto& cameraKeys = world.add<fggl::input::FreeCamKeys>(prototype); + cameraKeys.forward = glfwGetKeyScancode(GLFW_KEY_W); + cameraKeys.backward = glfwGetKeyScancode(GLFW_KEY_S); + cameraKeys.left = glfwGetKeyScancode(GLFW_KEY_A); + cameraKeys.right = glfwGetKeyScancode(GLFW_KEY_D); + cameraKeys.rotate_cw = glfwGetKeyScancode(GLFW_KEY_Q); + cameraKeys.rotate_ccw = glfwGetKeyScancode(GLFW_KEY_E); } -static fggl::ecs3::entity_t setupTerrain(fggl::ecs3::World& world) { - fggl::ecs3::entity_t terrain; +static fggl::entity::EntityID setupTerrain(fggl::entity::EntityManager& world) { + fggl::entity::EntityID terrain; { - terrain = world.create(false); + terrain = world.create(); - auto* camTf = world.add<fggl::math::Transform>(terrain); - camTf->origin( glm::vec3(-128.0f, 0.0f, 128.0f) ); + auto& camTf = world.add<fggl::math::Transform>(terrain); + camTf.origin( glm::vec3(-128.0f, 0.0f, 128.0f) ); //auto terrainData = m_world.get<fggl::data::HeightMap>(terrain); fggl::data::HeightMap terrainData{}; @@ -102,34 +98,35 @@ static fggl::ecs3::entity_t setupTerrain(fggl::ecs3::World& world) { const siv::PerlinNoise::seed_type seed = 123456U; const siv::PerlinNoise perlin{ seed }; - for (int z = 0; z < fggl::data::heightMaxZ; ++z) { - for (int x = 0; x < fggl::data::heightMaxX; ++x) { + for (std::size_t z = 0; z < fggl::data::heightMaxZ; ++z) { + for (std::size_t x = 0; x < fggl::data::heightMaxX; ++x) { const double noise = perlin.octave2D_11( (x * 0.01), (z * 0.01) , 4) * 10.f; terrainData.setValue(x, z, (float)noise); } } - world.set<fggl::data::HeightMap>(terrain, &terrainData); + + // world.set<fggl::data::HeightMap>(terrain, &terrainData); } return terrain; } -static fggl::ecs3::entity_t setupEnvironment(fggl::ecs3::World& world) { - auto& types = world.types(); +static fggl::entity::EntityID setupEnvironment(fggl::entity::EntityManager& world) { setupCamera(world); return setupTerrain(world); } -static fggl::ecs3::entity_t setupBunkerPrototype(fggl::ecs3::World& world) { - auto& types = world.types(); - fggl::ecs3::entity_t bunker; +static auto BUNKER_PROTOTYPE = "bunker"_fid; + +static void setupBunkerPrototype(fggl::entity::EntityFactory* factory) { { - bunker = world.create(true); - world.add(bunker, types.find(fggl::math::Transform::name)); + auto bunkerSpec = fggl::entity::EntitySpec{}; + bunkerSpec.components[fggl::util::make_guid("transform")] = {}; + + fggl::entity::ComponentSpec procMesh{}; + procMesh.set<std::string>("shader", "phong"); // mesh int nSections = 2; - constexpr float HALF_PI = M_PI / 2.0f; - constexpr char shader[] = "phong"; fggl::data::Mesh mesh; for (int j=-(nSections/2); j<=nSections/2; j++) { @@ -138,10 +135,10 @@ static fggl::ecs3::entity_t setupBunkerPrototype(fggl::ecs3::World& world) { const auto cubeMat = glm::translate( fggl::math::mat4( 1.0f ) , shapeOffset ); const auto leftSlope = fggl::math::modelMatrix( glm::vec3(-1.0f, 0.0f, 0.0f) + shapeOffset, - glm::vec3( 0.0f, -HALF_PI, 0.0f) ); + glm::vec3( 0.0f, -fggl::math::HALF_PI, 0.0f) ); const auto rightSlope = fggl::math::modelMatrix( glm::vec3( 1.0f, 0.0f, 0.0f) + shapeOffset, - glm::vec3( 0.0f, HALF_PI, 0.0f) ); + glm::vec3( 0.0f, fggl::math::HALF_PI, 0.0f) ); fggl::data::make_cube( mesh, cubeMat ); fggl::data::make_slope( mesh, leftSlope ); @@ -149,17 +146,21 @@ static fggl::ecs3::entity_t setupBunkerPrototype(fggl::ecs3::World& world) { } mesh.removeDups(); - fggl::data::StaticMesh staticMesh{mesh, shader}; - world.set<fggl::data::StaticMesh>(bunker, &staticMesh); + procMesh.set<std::vector<fggl::data::Vertex>>("vertexData", mesh.vertexList()); + procMesh.set<std::vector<unsigned int>>("indexData", mesh.indexList()); + bunkerSpec.components[fggl::util::make_guid("procedural_mesh")] = procMesh; + + factory->define(BUNKER_PROTOTYPE, bunkerSpec); } - return bunker; } void GameScene::setup() { m_canvas.size( fggl::math::vec2(0,0), fggl::math::vec2(100, 100)); + auto* entityFactory = m_owner.service<fggl::entity::EntityFactory>(); + auto terrain = setupEnvironment(world()); - auto bunkerPrototype = setupBunkerPrototype(world()); + setupBunkerPrototype(entityFactory); // create building prototype int nCubes = 3; @@ -167,14 +168,12 @@ void GameScene::setup() { glm::vec3 location; location.x = i * 6.f + 1.0f; location.z = -5.0f + 1.0f; - placeObject(world(), terrain, bunkerPrototype, location); + placeObject(world(), terrain, entityFactory, BUNKER_PROTOTYPE, location); } } void GameScene::update() { Game::update(); - - auto& inputSystem = input(); process_camera(world(), input()); } diff --git a/demo/demo/main.cpp b/demo/demo/main.cpp index a09e849aa03e51d94e2fa8d96c05bc25c99c4a49..24106bb9de13a7ff6055d965716fe0a5ab5452a5 100644 --- a/demo/demo/main.cpp +++ b/demo/demo/main.cpp @@ -26,7 +26,7 @@ #endif #include "fggl/fggl.hpp" -#include "fggl/ecs/component_fwd.hpp" +#include "fggl/entity/module.hpp" #include "fggl/audio/openal/audio.hpp" @@ -84,6 +84,7 @@ int main(int argc, const char* argv[]) { moduleManager.use<fggl::gfx::OpenGL4>(); moduleManager.use<fggl::display::GLFW>(); moduleManager.use<fggl::assets::AssetFolders>(); + moduleManager.use<fggl::entity::ECS>(); moduleManager.resolve(); // create the application @@ -99,10 +100,10 @@ int main(int argc, const char* argv[]) { // load a bunch of modules to provide game functionality //app.use<fggl::ecs3::ecsTypes>(); - app.use<fggl::gfx::SceneUtils>(); + /*app.use<fggl::gfx::SceneUtils>(); #ifdef FGGL_MODULE_BULLET app.use<FGGL_MODULE_BULLET>(); - #endif + #endif*/ // our test states setup_menu(app); diff --git a/demo/demo/rollball.cpp b/demo/demo/rollball.cpp index 9967e3091f8b54475a6c2bfd65ea9e786c05b8bc..88cc9269175199e4af3d489b866edfbb5b338063 100644 --- a/demo/demo/rollball.cpp +++ b/demo/demo/rollball.cpp @@ -23,83 +23,65 @@ #include "fggl/gfx/phong.hpp" #include "fggl/input/camera_input.hpp" -#include "fggl/ecs3/prototype/loader.hpp" -#include "fggl/debug/draw.hpp" - -#include <array> - -struct Prefabs { - fggl::ecs3::entity_t collectable; - fggl::ecs3::entity_t player; -}; -static void setup_prefabs(fggl::data::Storage* storage, fggl::ecs3::World& world, Prefabs& prefabs) { - fggl::ecs3::load_prototype_file(world, *storage, "rollball.yml"); - - { - // player (cube because my sphere function doesn't exist yet - prefabs.player = world.findPrototype("player"); - world.add<fggl::phys::Dynamics>(prefabs.player); - } +#include "fggl/entity/entity.hpp" +#include "fggl/entity/loader/loader.hpp" - { - // collectable - prefabs.collectable = world.findPrototype("collectable"); +#include "fggl/debug/draw.hpp" - // we need both of these for callbacks to trigger. - world.add<fggl::phys::CollisionCallbacks>(prefabs.collectable); - world.add<fggl::phys::CollisionCache>(prefabs.collectable); - } +#include <array> -} +static const fggl::util::GUID playerPrefab = "player"_fid; +static const fggl::util::GUID collectablePrefab = "collectable"_fid; +static const fggl::util::GUID WallNPrefab = "wallX"_fid; +static const fggl::util::GUID WallEPrefab = "wallZ"_fid; +static const fggl::util::GUID floorPrefab = "floor"_fid; -static void setup_camera(fggl::ecs3::World& world) { - auto prototype = world.create(false); +static void setup_camera(fggl::entity::EntityManager& world) { + auto prototype = world.create(); // setup camera position/transform - auto* transform = world.add<fggl::math::Transform>(prototype); - if ( transform != nullptr) { - transform->origin(glm::vec3(10.0f, 3.0f, 10.0f)); - } + auto& transform = world.add<fggl::math::Transform>(prototype); + transform.origin(glm::vec3(10.0f, 3.0f, 10.0f)); // setup camera components world.add<fggl::gfx::Camera>(prototype); } -static fggl::ecs3::entity_t setup_environment(fggl::ecs3::World& world, const fggl::math::vec2& size, demo::RollState& state) { +static fggl::entity::EntityID setup_environment(fggl::entity::EntityManager& world, fggl::entity::EntityFactory* factory, const fggl::math::vec2& size, demo::RollState& state) { { - auto northWall = world.createFromPrototype("wallX"); - auto* transform = world.get<fggl::math::Transform>(northWall); - transform->origin({size.x/2, 0.0F, 0.0F}); + auto northWall = factory->create(WallNPrefab, world); + auto& transform = world.get<fggl::math::Transform>(northWall); + transform.origin({size.x/2, 0.0F, 0.0F}); } { - auto southWall = world.createFromPrototype("wallX"); - auto* transform = world.get<fggl::math::Transform>(southWall); - transform->origin({-size.x/2, 0.0F, 0.0F}); + auto southWall = factory->create(WallNPrefab, world); + auto& transform = world.get<fggl::math::Transform>(southWall); + transform.origin({-size.x/2, 0.0F, 0.0F}); } { - auto westWall = world.createFromPrototype("wallZ"); - auto* transform = world.get<fggl::math::Transform>(westWall); - transform->origin({0.0F, 0.0F, -size.y/2}); + auto westWall = factory->create(WallEPrefab, world); + auto& transform = world.get<fggl::math::Transform>(westWall); + transform.origin({0.0F, 0.0F, -size.y/2}); } { - auto eastWall = world.createFromPrototype("wallZ"); - auto* transform = world.get<fggl::math::Transform>(eastWall); - transform->origin({0.0F, 0.0F, size.y/2}); + auto eastWall = factory->create(WallEPrefab, world); + auto& transform = world.get<fggl::math::Transform>(eastWall); + transform.origin({0.0F, 0.0F, size.y/2}); } { - auto floor = world.createFromPrototype("floor"); - auto *transform = world.get<fggl::math::Transform>(floor); - transform->origin({0.0F, -2.5F, 0.0F}); + auto floor = factory->create(floorPrefab, world); + auto& transform = world.get<fggl::math::Transform>(floor); + transform.origin({0.0F, -2.5F, 0.0F}); } { // player just starts off as the prefab dictates - state.player = world.createFromPrototype("player"); + state.player = factory->create(playerPrefab, world); } { @@ -113,16 +95,16 @@ static fggl::ecs3::entity_t setup_environment(fggl::ecs3::World& world, const fg // build the collectables int collectCount = 0; for (auto& pos : collectPos) { - auto collectable = world.createFromPrototype("collectable"); - auto* transform = world.get<fggl::math::Transform>(collectable); - transform->origin(pos); + auto collectable = factory->create(collectablePrefab, world); + auto& transform = world.get<fggl::math::Transform>(collectable); + transform.origin(pos); state.collectables[collectCount++] = collectable; } } // ensure the state is clean - state.closestPickup = fggl::ecs::NULL_ENTITY; + state.closestPickup = fggl::entity::INVALID; state.mode = demo::DebugMode::NORMAL; state.time = 0.0F; @@ -144,26 +126,28 @@ namespace demo { void RollBall::activate() { Game::activate(); - Prefabs prefabs{}; - setup_prefabs(m_owner.service<fggl::data::Storage>(), world(), prefabs); + fggl::debug::log(fggl::debug::Level::info, "RollBall::activate()"); + + auto* assetLoader = m_owner.service<fggl::assets::Loader>(); + assetLoader->load("rollball.yml", fggl::entity::PROTOTYPE_ASSET); // collectable callbacks - auto* collectableCallbacks = world().get<fggl::phys::CollisionCallbacks>(prefabs.collectable); + /*auto* collectableCallbacks = world().get<fggl::phys::CollisionCallbacks>(prefabs.collectable); collectableCallbacks->onEnter = [this](auto ourEntity, auto theirEntity) { if ( theirEntity == state.player) { //if ( ourEntity == state.closestPickup ) { // // we're the closest pickup and we're about to get killed, so need to not be. // state.closestPickup = fggl::ecs::NULL_ENTITY; //} - this->world().destroy(ourEntity); + world().destroy(ourEntity); } - }; + };*/ // actual scene objects setup_camera(world()); // create a 20x20 grid - setup_environment(world(), WORLD_SIZE, state); + setup_environment(world(), m_owner.service<fggl::entity::EntityFactory>(), WORLD_SIZE, state); } fggl::math::vec3 calc_move_vector(const fggl::input::Input& input) { @@ -197,7 +181,7 @@ namespace demo { auto& input = this->input(); - if ( state.player != fggl::ecs3::NULL_ENTITY ) { + if ( state.player != fggl::entity::INVALID ) { auto &world = this->world(); // mode selection @@ -209,26 +193,26 @@ namespace demo { auto force = calc_move_vector(input); if ( force != fggl::math::VEC3_ZERO ) { force = glm::normalize(force) * MOVE_FORCE; - auto *dynamics = world.get<fggl::phys::Dynamics>(state.player); - dynamics->force += force; + auto& dynamics = world.get<fggl::phys::Dynamics>(state.player); + dynamics.force += force; } // track player position with camera { - auto cameras = world.findMatching<fggl::gfx::Camera>(); - fggl::ecs3::entity_t cam = cameras[0]; - auto *camComp = world.get<fggl::gfx::Camera>(cam); - camComp->target = world.get<fggl::math::Transform>(state.player)->origin(); + auto cameras = world.find<fggl::gfx::Camera>(); + auto cam = cameras[0]; + auto& camComp = world.get<fggl::gfx::Camera>(cam); + camComp.target = world.get<fggl::math::Transform>(state.player).origin(); - auto *camTransform = world.get<fggl::math::Transform>(cam); - camTransform->origin( camComp->target + cameraOffset ); + auto& camTransform = world.get<fggl::math::Transform>(cam); + camTransform.origin( camComp.target + cameraOffset ); } // distance mode if ( state.mode == DebugMode::DISTANCE ) { closestPickup(world); } else { - if ( state.closestPickup != fggl::ecs::NULL_ENTITY ) { + if ( state.closestPickup != fggl::entity::INVALID ) { if ( world.alive(state.closestPickup) ){ auto *renderer = world.tryGet<fggl::gfx::PhongMaterial>(state.closestPickup); if (renderer != nullptr) { @@ -236,7 +220,7 @@ namespace demo { } } - state.closestPickup = fggl::ecs::NULL_ENTITY; + state.closestPickup = fggl::entity::INVALID; } } @@ -246,18 +230,18 @@ namespace demo { } } - void RollBall::closestPickup(fggl::ecs3::World &world) { + void RollBall::closestPickup(fggl::entity::EntityManager &world) { float closestDistance = FLT_MAX; - fggl::ecs::entity_t closestEntity = fggl::ecs::NULL_ENTITY; - auto playerPos = world.get<fggl::math::Transform>(state.player)->origin(); + auto closestEntity = fggl::entity::INVALID; + auto playerPos = world.get<fggl::math::Transform>(state.player).origin(); for ( const auto& pickup : state.collectables ) { if ( !world.alive(pickup) ) { continue; } - auto* transform = world.get<fggl::math::Transform>(pickup); - auto distance = glm::distance(transform->origin(), playerPos); + auto& transform = world.get<fggl::math::Transform>(pickup); + auto distance = glm::distance(transform.origin(), playerPos); if ( distance < closestDistance) { closestDistance = distance; closestEntity = pickup; @@ -275,7 +259,7 @@ namespace demo { // now, deal with the new closest state.closestPickup = closestEntity; - if (closestEntity != fggl::ecs::NULL_ENTITY) { + if (closestEntity != fggl::entity::INVALID) { auto *renderer = world.tryGet<fggl::gfx::PhongMaterial>(state.closestPickup); if (renderer != nullptr) { renderer->diffuse = COLOUR_BLUE; @@ -284,18 +268,18 @@ namespace demo { } // closest pickup should face the player - if ( state.closestPickup != fggl::ecs::NULL_ENTITY) { - auto* transform = world.get<fggl::math::Transform>(state.closestPickup); - auto* playerTransform = world.get<fggl::math::Transform>( state.player ); - transform->lookAt(playerTransform->origin()); + if ( state.closestPickup != fggl::entity::INVALID) { + auto& transform = world.get<fggl::math::Transform>(state.closestPickup); + auto& playerTransform = world.get<fggl::math::Transform>( state.player ); + transform.lookAt(playerTransform.origin()); // lazy, so using the debug draw line for this bit - dd::line( &playerTransform->origin()[0], &transform->origin()[0], dd::colors::White ); + dd::line( &playerTransform.origin()[0], &transform.origin()[0], dd::colors::White ); } } - void RollBall::spinCubes(fggl::ecs3::World& world, float deltaTime) { + void RollBall::spinCubes(fggl::entity::EntityManager& world, float deltaTime) { // rotation for ( const auto& entity : state.collectables ) { if ( !world.alive(entity) || entity == state.closestPickup ) { @@ -303,8 +287,8 @@ namespace demo { } // rotate the cubes - auto* transform = world.get<fggl::math::Transform>(entity); - transform->rotateEuler( COLLECTABLE_ROTATION * deltaTime ); + auto& transform = world.get<fggl::math::Transform>(entity); + transform.rotateEuler( COLLECTABLE_ROTATION * deltaTime ); } } diff --git a/demo/demo/topdown.cpp b/demo/demo/topdown.cpp index 25f7a239a7d11217244bb59a85c47ec7af47cdd7..c424dc3652050555d7a87e19f5be1a526f521553 100644 --- a/demo/demo/topdown.cpp +++ b/demo/demo/topdown.cpp @@ -21,35 +21,36 @@ #include "fggl/data/storage.hpp" #include "fggl/gfx/camera.hpp" #include "fggl/input/camera_input.hpp" -#include "fggl/ecs3/prototype/loader.hpp" +#include "fggl/entity/loader/loader.hpp" + +static const fggl::util::GUID collectablePrefab = "collectable"_fid; +static const fggl::util::GUID WallNPrefab = "wallN"_fid; +static const fggl::util::GUID WallEPrefab = "wallE"_fid; +static const fggl::util::GUID floorPrefab = "floor"_fid; namespace demo { - static void create_topdown_camera(fggl::ecs3::World& world) { - auto prototype = world.create(false); + static void create_topdown_camera(fggl::entity::EntityManager& world) { + auto prototype = world.create(); // setup camera position/transform - auto* transform = world.add<fggl::math::Transform>(prototype); - if ( transform != nullptr) { - transform->origin(glm::vec3(10.0f, 50.0f, 10.0f)); - } + auto& transform = world.add<fggl::math::Transform>(prototype); + transform.origin(glm::vec3(10.0f, 50.0f, 10.0f)); // setup camera components - auto* camera = world.add<fggl::gfx::Camera>(prototype); - camera->target = glm::vec3(0.0f, 0.0f, 0.0f); - - auto* cameraKeys = world.add<fggl::input::FreeCamKeys>(prototype); - if ( cameraKeys != nullptr ) { - cameraKeys->forward = glfwGetKeyScancode(GLFW_KEY_W); - cameraKeys->backward = glfwGetKeyScancode(GLFW_KEY_S); - cameraKeys->left = glfwGetKeyScancode(GLFW_KEY_A); - cameraKeys->right = glfwGetKeyScancode(GLFW_KEY_D); - cameraKeys->rotate_cw = glfwGetKeyScancode(GLFW_KEY_Q); - cameraKeys->rotate_ccw = glfwGetKeyScancode(GLFW_KEY_E); - } + auto& camera = world.add<fggl::gfx::Camera>(prototype); + camera.target = glm::vec3(0.0f, 0.0f, 0.0f); + + auto& cameraKeys = world.add<fggl::input::FreeCamKeys>(prototype); + cameraKeys.forward = glfwGetKeyScancode(GLFW_KEY_W); + cameraKeys.backward = glfwGetKeyScancode(GLFW_KEY_S); + cameraKeys.left = glfwGetKeyScancode(GLFW_KEY_A); + cameraKeys.right = glfwGetKeyScancode(GLFW_KEY_D); + cameraKeys.rotate_cw = glfwGetKeyScancode(GLFW_KEY_Q); + cameraKeys.rotate_ccw = glfwGetKeyScancode(GLFW_KEY_E); } - static void place_cover_boxes(fggl::ecs3::World& world) { + static void place_cover_boxes(fggl::entity::EntityFactory* factory, fggl::entity::EntityManager& world) { std::array<fggl::math::vec3,8> boxPos = {{ {-10.0F, 0.0F, -10.0F}, {-10.0F, 0.0F, 10.0F}, @@ -61,52 +62,51 @@ namespace demo { { 0.0F, 0.0F, 10.0F}, }}; for (auto pos : boxPos) { - auto box = world.createFromPrototype("collectable"); - auto* transform = world.get<fggl::math::Transform>(box); - transform->origin(pos); + auto box = factory->create(collectablePrefab, world); + auto& transform = world.get<fggl::math::Transform>(box); + transform.origin(pos); } } - static void build_arena(fggl::ecs3::World& world) { + static void build_arena(fggl::entity::EntityFactory* factory, fggl::entity::EntityManager& world) { { - auto floor = world.createFromPrototype("floor"); - auto* transform = world.get<fggl::math::Transform>(floor); - transform->origin({0.0F, -2.5F, 0.0F}); - fggl::debug::log("created floor: {}", floor); + auto floor = factory->create(floorPrefab, world); + auto& transform = world.get<fggl::math::Transform>(floor); + transform.origin({0.0F, -2.5F, 0.0F}); + //fggl::debug::log("created floor: {}", floor); } fggl::math::vec2 size{40.0F, 40.0F}; for (auto side : {-1.0F, 1.0F}) { { - auto northWall = world.createFromPrototype("wallX"); - auto *transform = world.get<fggl::math::Transform>(northWall); - transform->origin({size.x / 2 * side, 0.0F, 0.0F}); + auto obj = factory->create(WallNPrefab, world); + auto& transform = world.get<fggl::math::Transform>(obj); + transform.origin({size.x / 2 * side, 0.0F, 0.0F}); } { - auto westWall = world.createFromPrototype("wallZ"); - auto* transform = world.get<fggl::math::Transform>(westWall); - transform->origin({0.0F, 0.0F, size.y/2 * side }); + auto obj = factory->create(WallEPrefab, world); + auto& transform = world.get<fggl::math::Transform>(obj); + transform.origin({0.0F, 0.0F, size.y/2 * side }); } } - place_cover_boxes(world); + place_cover_boxes(factory, world); } -static void process_camera(fggl::ecs3::World& ecs, const fggl::input::Input& input) { - auto cameras = ecs.findMatching<fggl::gfx::Camera>(); +static void process_camera(fggl::entity::EntityManager& ecs, const fggl::input::Input& input) { + auto cameras = ecs.find<fggl::gfx::Camera>(); if ( !cameras.empty() ) { - fggl::ecs3::entity_t cam = cameras[0]; + auto cam = cameras[0]; fggl::input::process_scroll(ecs, input, cam); fggl::input::process_freecam(ecs, input, cam); fggl::input::process_edgescroll(ecs, input, cam); } } -static void populate_sample_level(fggl::ecs3::World& world) { +static void populate_sample_level(fggl::entity::EntityFactory* factory, fggl::entity::EntityManager& world) { create_topdown_camera(world); - - build_arena(world); + build_arena(factory, world); } TopDown::TopDown(fggl::App& app) : fggl::scenes::Game(app) { @@ -116,11 +116,11 @@ TopDown::TopDown(fggl::App& app) : fggl::scenes::Game(app) { void TopDown::activate() { Game::activate(); - auto* storage = m_owner.service<fggl::data::Storage>(); - fggl::ecs3::load_prototype_file(world(), *storage, "topdown.yml"); + auto* factory = m_owner.service<fggl::entity::EntityFactory>(); + //fggl::ecs3::load_prototype_file(world(), *storage, "topdown.yml"); // create a sample level - populate_sample_level(world()); + populate_sample_level(factory, world()); } void TopDown::update() { @@ -133,12 +133,12 @@ void TopDown::update() { } void TopDown::pick_object() { - auto cameras = world().findMatching<fggl::gfx::Camera>(); + auto cameras = world().find<fggl::gfx::Camera>(); if ( cameras.empty() ) { return; } - fggl::ecs3::entity_t cam = cameras[0]; + auto cam = cameras[0]; fggl::math::vec2 position { input().mouse.axis(fggl::input::MouseAxis::X), @@ -147,8 +147,8 @@ void TopDown::pick_object() { auto ray = fggl::gfx::get_camera_ray(world(), cam, position); auto hit = phys().raycast(ray); - if ( hit != fggl::ecs3::NULL_ENTITY) { - fggl::debug::log("hit: {}", hit); + if ( hit != fggl::entity::INVALID) { + //fggl::debug::log("hit: {}", hit); } else { fggl::debug::log("no hit"); } diff --git a/demo/include/rollball.hpp b/demo/include/rollball.hpp index 1934a67a1afc000a4c9ac433faac13b04a96504c..d18bb4bac235b7b5525a07b1e2d4da347d07b71d 100644 --- a/demo/include/rollball.hpp +++ b/demo/include/rollball.hpp @@ -36,14 +36,14 @@ namespace demo { }; struct RollState { - fggl::ecs::entity_t player = fggl::ecs::NULL_ENTITY; - fggl::ecs::entity_t closestPickup; + fggl::entity::EntityID player = fggl::entity::INVALID; + fggl::entity::EntityID closestPickup; DebugMode mode = DebugMode::NORMAL; - std::array<fggl::ecs3::entity_t, 3> collectables { - fggl::ecs::NULL_ENTITY, - fggl::ecs::NULL_ENTITY, - fggl::ecs::NULL_ENTITY }; + std::array<fggl::entity::EntityID, 3> collectables { + fggl::entity::INVALID, + fggl::entity::INVALID, + fggl::entity::INVALID }; float time = 0.0f; }; @@ -63,8 +63,8 @@ namespace demo { RollState state; fggl::math::vec3 cameraOffset = {-15.0F, 15.0F, 0.0F}; - void closestPickup(fggl::ecs3::World& world); - void spinCubes(fggl::ecs3::World& world, float dt); + void closestPickup(fggl::entity::EntityManager& world); + void spinCubes(fggl::entity::EntityManager& world, float dt); }; } diff --git a/fggl/CMakeLists.txt b/fggl/CMakeLists.txt index d7c20d199fcb7ff0c09edae4876fc100753f7d62..64fa463ae42b8301ca3b3440b6d43d408aa2e08d 100644 --- a/fggl/CMakeLists.txt +++ b/fggl/CMakeLists.txt @@ -43,12 +43,6 @@ target_sources(${PROJECT_NAME} data/procedural.cpp data/heightmap.cpp - ecs/ecs.cpp - ecs3/module/module.cpp - ecs3/fast/Container.cpp - ecs3/prototype/loader.cpp - ecs3/prototype/world.cpp - scenes/menu.cpp scenes/game.cpp @@ -88,6 +82,7 @@ add_subdirectory(debug) # platform integrations add_subdirectory(platform) +add_subdirectory(entity) ## # begin windows support diff --git a/fggl/app.cpp b/fggl/app.cpp index b2b4ee4b72f10f5bb57d6c7343c0a41e6047d6af..406fbc757a50bb42246e045b46c7587052d84e63 100644 --- a/fggl/app.cpp +++ b/fggl/app.cpp @@ -16,21 +16,15 @@ #include <memory> #include <spdlog/spdlog.h> - #include <fggl/app.hpp> -#include <fggl/ecs3/types.hpp> -#include "fggl/ecs/component_fwd.hpp" -#include "fggl/ecs3/module/module.hpp" namespace fggl { App::App(modules::Manager* services, const Identifer& name) : App::App( services, name, name ) { } - App::App(modules::Manager* services, const Identifer& name, const Identifer& folder ) : + App::App(modules::Manager* services, const Identifer& /*name*/, const Identifer& /*folder*/ ) : m_running(true), - m_types(std::make_unique<ecs3::TypeRegistry>()), - m_modules(std::make_unique<ecs3::ModuleManager>(*m_types)), m_window(nullptr), m_states(), m_subsystems(services){} @@ -58,14 +52,14 @@ namespace fggl { if ( windowing != nullptr) { windowing->pollEvents(); } - m_modules->onUpdate(); + //m_modules->onUpdate(); auto& state = m_states.active(); state.update(); // window rendering to frame buffer if ( m_window != nullptr ) { - m_modules->onFrameStart(); + // m_modules->onFrameStart(); m_window->frameStart(); // get draw instructions @@ -73,7 +67,7 @@ namespace fggl { state.render(graphics); m_window->frameEnd(); - m_modules->onFrameEnd(); + // m_modules->onFrameEnd(); m_running = m_running && !m_window->wantClose(); } diff --git a/fggl/data/heightmap.cpp b/fggl/data/heightmap.cpp index 4f401f17848230e0c7d93add456b2595c4bb786c..2122f081b0d16cf2003434f76d409a340cb283b6 100644 --- a/fggl/data/heightmap.cpp +++ b/fggl/data/heightmap.cpp @@ -83,7 +83,7 @@ namespace fggl::data { delete[] triNormals; } - void generateHeightMesh(const data::HeightMap *heights, data::Mesh &mesh) { + void generateHeightMesh(const data::HeightMap& heights, data::Mesh &mesh) { // step 1: convert height data into vertex locations const int numElms = data::heightMaxX * data::heightMaxZ; @@ -92,7 +92,7 @@ namespace fggl::data { // iterate the for (std::size_t x = 0; x < data::heightMaxX; x++) { for (std::size_t z = 0; z < data::heightMaxZ; z++) { - float level = heights->getValue(x, z); + float level = heights.getValue(x, z); auto xPos = float(x); auto zPos = float(z); diff --git a/fggl/ecs/ecs.cpp b/fggl/ecs/ecs.cpp deleted file mode 100644 index cda2001366735ceab99023376f1c868b53837513..0000000000000000000000000000000000000000 --- a/fggl/ecs/ecs.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file is part of FGGL. - * - * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with FGGL. - * If not, see <https://www.gnu.org/licenses/>. - */ - -#include <fggl/ecs/ecs.hpp> - -#include <iostream> - -using namespace fggl::ecs; - -Archetype::Archetype(const archToken_t& token ) : type(token) { - for ( archToken_t::size_type i = 0; i < token.size(); ++i ) { - data.push_back( new unsigned char[default_cap] ); - dataSizes.push_back( default_cap ); - } -} - -ECS::ECS() : m_entityIDCounter(1) {} - -ECS::~ECS() { - for( Archetype* arch : m_archetypes ) { - for ( std::size_t i=0; i < arch->type.size(); ++i ) { - const ComponentBase* const comp = m_componentMap[arch->type[i]]; - const std::size_t& size = comp->size(); - - for ( std::size_t e=0; e<arch->entities.size(); ++e ) { - comp->destroy(&arch->data[i][e*size]); - } - delete[] arch->data[i]; - } - delete arch; - } - - for( componentmap_t::value_type& p : m_componentMap ) - delete p.second; -} - -entity_t ECS::getNewID() { - return m_entityIDCounter++; -} - -entity_t ECS::createEntity( ) { - auto eid = getNewID(); - - Record dummy; - dummy.archetype = nullptr; - dummy.index = 0; - m_entityArchtypes[ eid ] = dummy; - - return eid; -} - -Archetype* ECS::getArchetype(const archToken_t& id) { - for ( auto* arch : m_archetypes ) { - if ( arch->type == id ) { - return arch; - } - } - - // didn't exist, need to make one - Archetype* arch = new Archetype(id); - m_archetypes.push_back( arch ); - - return arch; -} diff --git a/fggl/ecs3/fast/Container.cpp b/fggl/ecs3/fast/Container.cpp deleted file mode 100644 index 182df0ae4cd0094947c7c7e63d8119a8188115e6..0000000000000000000000000000000000000000 --- a/fggl/ecs3/fast/Container.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* - * This file is part of FGGL. - * - * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with FGGL. - * If not, see <https://www.gnu.org/licenses/>. - */ - -// -// Created by webpigeon on 23/10/2021. -// - -#include <fggl/ecs3/fast/Container.hpp> - -namespace fggl::ecs3 { - - std::ostream& operator<<(std::ostream& out, RecordIdentifier const& curr) { - out << "record("; - for (std::size_t i=0; i<curr.count; i++) { - out << i; - if ( i != curr.count-1) { - out << ","; - } - } - out << ")"; - return out; - } - - - std::size_t Container::create() { - ensure(m_size + 1); - auto pos = m_size++; - - // setup entry - for (std::size_t i = 0; i < m_identifier.count; ++i) { - auto compMeta = m_types.meta(m_identifier.types[i]); - auto offset = offsets[i] + (pos * compMeta->size()); - - auto compPtr = backingStore + offset; - compMeta->construct(compPtr); - } - return pos; - } - - void Container::remove(std::size_t pos) { - // cleanup entry - for (std::size_t i = 0; i < m_identifier.count; ++i) { - auto compMeta = m_types.meta(m_identifier.types[i]); - auto offset = offsets[i] + (pos * compMeta->size()); - - auto compPtr = backingStore + offset; - compMeta->destroy(compPtr); - } - - // if we're not the last one, swap places - if (pos != m_size - 1) { - move(pos, *this, m_size - 1); - } - - m_size--; - } - - void Container::move(std::size_t newPos, Container &oldContainer, std::size_t oldPos) { - for (std::size_t i = 0; i < m_identifier.count; ++i) { - auto thisComp = m_identifier.types[i]; - auto compMeta = m_types.meta(thisComp); - - auto newPtr = backingStore + (offsets[i] + (newPos * compMeta->size())); - auto oldPtr = oldContainer.data_raw(thisComp) + (oldPos * compMeta->size()); - compMeta->move(oldPtr, newPtr); - compMeta->destroy(oldPtr); - } - } - - std::size_t Container::expand(Container &other, std::size_t otherPos, component_type_t newComp) { - ensure(m_size + 1); - auto pos = m_size++; - - for (std::size_t i = 0; i < m_identifier.count; ++i) { - auto thisComp = m_identifier.types[i]; - auto compMeta = m_types.meta(thisComp); - - auto newPtr = backingStore + (offsets[i] + (pos * compMeta->size())); - if (newComp != thisComp) { - // we're moving the component - auto oldPtr = other.data_raw(thisComp) + (otherPos * compMeta->size()); - compMeta->move(oldPtr, newPtr); - } else { - // this is the new comp - compMeta->construct(newPtr); - } - } - - // remove the old entity - other.remove(otherPos); - return pos; - } - - void Container::contract(Container &other, std::size_t otherPos) { - ensure(m_size + 1); - auto pos = m_size++; - - move(pos, other, otherPos); - - for (std::size_t i = 0; i < m_identifier.count; ++i) { - auto thisComp = m_identifier.types[i]; - auto compMeta = m_types.meta(thisComp); - - auto newPtr = backingStore + (offsets[i] + (pos * compMeta->size())); - auto oldPtr = other.data_raw(thisComp) + (otherPos * compMeta->size()); - compMeta->move(oldPtr, newPtr); - } - - other.remove(otherPos); - } - - void Container::ensure(std::size_t size) { - if (size < m_capacity) { - return; - } - - std::size_t required = 0; - for (std::size_t i = 0; i < m_identifier.count; i++) { - auto meta = m_types.meta(m_identifier.types[i]); - required += meta->size() * size; - } - - auto *newBacking = new unsigned char[required]; - std::size_t newOffsets[RecordIdentifier::MAX_COMPS]; - std::size_t currOffset = 0; - - // bulk copy routine - for (std::size_t cid = 0; cid < m_identifier.count; ++cid) { - auto compMeta = m_types.meta(m_identifier.types[cid]); - newOffsets[cid] = currOffset; - - compMeta->bulkMove( - backingStore + offsets[cid], - newBacking + newOffsets[cid], - m_size); - - currOffset += size * compMeta->size(); - } - - // swap the backing store and cleanup the old one - auto *tmp = backingStore; - backingStore = newBacking; - delete[] tmp; - - // update size info - std::memcpy(offsets, newOffsets, m_identifier.count * sizeof(std::size_t)); - m_capacity = size; - } - -} diff --git a/fggl/ecs3/prototype/loader.cpp b/fggl/ecs3/prototype/loader.cpp deleted file mode 100644 index 5c5ca6fd37143d8dd4785b7f7ca8b6a46d2f5cd4..0000000000000000000000000000000000000000 --- a/fggl/ecs3/prototype/loader.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "fggl/ecs3/prototype/loader.hpp" - -namespace fggl::ecs3 { - - void load_prototype_node( ecs3::World& world, const YAML::Node& node) { - auto prototype = world.create(true); - auto& typeReg = world.types(); - - // store the prototype's name for future use - auto* nameComp = world.get<ecs3::EntityMeta>( prototype ); - nameComp->typeName = node["name"].as<std::string>(); - - // grab the components and iterate them - if ( node["components"] ) { - world.createFromSpec( prototype, node["components"]); - } - } - - void load_prototype_file( ecs3::World& world, data::Storage& storage, const std::string& name ) { - auto path = storage.resolvePath( data::Data, name ); - if ( !std::filesystem::exists(path) ) { - debug::log(debug::Level::warning, "prototype file not found: {}", path.string()); - return; - } - - auto root = YAML::LoadFile( path.string().c_str() ); - for (const auto& node : root["prefabs"] ) { - load_prototype_node(world, node); - } - } - -} - -namespace fggl::ecs { - - static void process_mesh(data::Mesh& out, const YAML::Node& shape) { - auto transform = data::OFFSET_NONE; - if ( shape["offset"] ) { - auto scaleFactor = shape["offset"].as<math::vec3>(); - transform = glm::translate(transform, scaleFactor); - } - - if ( shape["scale"] ) { - auto scaleFactor = shape["scale"].as<math::vec3>(); - transform = glm::scale(transform, scaleFactor); - } - - // now the shape itself - auto shapeType = shape["type"].as<std::string>(); - if ( shapeType == SHAPE_BOX_VALUE ) { - data::make_cube(out, transform); - } else if ( shapeType == SHAPE_SPHERE_VALUE ) { - int stacks = shape["stacks"].as<int>(DEFAULT_STACKS); - int slices = shape["slices"].as<int>(DEFAULT_SLICES); - data::make_sphere(out, transform, stacks, slices); - } - } - - template<> - bool restore_config(math::Transform* comp, const YAML::Node& node) { - comp->origin(node["origin"].as<math::vec3>( math::VEC3_ZERO )); - comp->euler(node["rotation"].as<math::vec3>(math::VEC3_ZERO)); - comp->scale(node["scale"].as<math::vec3>(math::VEC3_ONES)); - return true; - } - - template<> - bool restore_config(gfx::PhongMaterial* comp, const YAML::Node& node) { - comp->diffuse = node["diffuse"].as<math::vec3>( gfx::DEFAULT_DIFFUSE ); - comp->ambient = node["ambient"].as<math::vec3>( gfx::DEFAULT_AMBIENT ); - comp->specular = node["specular"].as<math::vec3>( gfx::DEFAULT_SPECULAR ); - comp->shininess = node["shininess"].as<float>( gfx::DEFAULT_SHININESS ); - return true; - } - - template<> - bool restore_config(data::StaticMesh* meshComp, const YAML::Node& node) { - if ( !node["pipeline"] ) { - return false; - } - - meshComp->pipeline = node["pipeline"].as<std::string>(); - - if ( node["shape"] ) { - data::Mesh mesh; - if (node["shape"].IsSequence()) { - // is a composite shape - for (const auto &shapeNode : node["shape"]) { - process_mesh(mesh, shapeNode); - } - } else { - // is a basic shape - process_mesh(mesh, node["shape"]); - } - - // optimise and load - mesh.removeDups(); - meshComp->mesh = mesh; - } else { - return false; - } - - return true; - } - - template<> - bool restore_config(phys::RigidBody* body, const YAML::Node& node) { - body->type = node["type"].as<fggl::phys::BodyType>(fggl::phys::BodyType::DYNAMIC); - if ( body->type == fggl::phys::BodyType::STATIC) { - body->mass = phys::MASS_STATIC; - } else { - body->mass = node["mass"].as<float>( phys::MASS_DEFAULT ); - } - - // shape detection - if ( node["shape"] ) { - auto type = node["shape"]["type"].as< std::string >(); - - if ( type == SHAPE_BOX_VALUE ) { - // assume unit box if extents are missing - auto extents = node["shape"]["extents"].as<math::vec3>(phys::UNIT_EXTENTS); - body->shape = new phys::Box(extents); - } else if ( type == SHAPE_SPHERE_VALUE ) { - auto radius = node["shape"]["radius"].as<float>(0.5F); - body->shape = new phys::Sphere(radius); - } else { - return false; - } - } - - return true; - } - - template<> - bool restore_config(phys::CollisionCallbacks* callbacks, const YAML::Node& node) { - // TODO implement callback scripting - return true; - } - - template<> - bool restore_config(phys::CollisionCache* cache, const YAML::Node& node){ - // probably nothing needed here. Ideally, this should be automatically added when callbacks are. - return true; - } -} diff --git a/fggl/entity/CMakeLists.txt b/fggl/entity/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..d1ab8fe68c683cb013bf9878117713459d9eb81c --- /dev/null +++ b/fggl/entity/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(fggl + PRIVATE + loader/loader.cpp + module.cpp +) diff --git a/fggl/entity/loader/loader.cpp b/fggl/entity/loader/loader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..62750d21ee7ef9b951e55a14bec84c8f4c8d4023 --- /dev/null +++ b/fggl/entity/loader/loader.cpp @@ -0,0 +1,55 @@ +/* + * This file is part of FGGL. + * + * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with FGGL. + * If not, see <https://www.gnu.org/licenses/>. + */ + +// +// Created by webpigeon on 24/07/22. +// + +#include "fggl/entity/loader/loader.hpp" +#include "fggl/debug/logging.hpp" + +namespace fggl::entity { + + assets::AssetRefRaw load_prototype(EntityFactory* factory, const assets::AssetGUID& guid, assets::AssetData data) { + auto* filePath = std::get<assets::AssetPath*>(data); + + // We need to process the prototypes, and load them into the asset system. + auto nodes = YAML::LoadAllFromFile( filePath->c_str() ); + for (const auto& node : nodes) { + auto prefabs = node["prefabs"]; + for ( const auto& prefab : prefabs ) { + auto name = prefab["name"].as<fggl::util::GUID>(); + + #ifndef NDEBUG + debug::log(debug::Level::info, "found prefab: {}", fggl::util::guidToString(name) ); + #endif + + // setup the components + EntitySpec entity{}; + for (const auto& compEntry : prefab["components"]) { + auto compId = compEntry.first.as<fggl::util::GUID>(); + + ComponentSpec compSpec{}; + compSpec.config = compEntry.second; + entity.components[compId] = compSpec; + } + + factory->define( name, entity ); + } + } + + return nullptr; + } + +} // namespace fggl::entity \ No newline at end of file diff --git a/fggl/entity/module.cpp b/fggl/entity/module.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ab79b1b211d3864e9c1f5848c69b5ea8bf195661 --- /dev/null +++ b/fggl/entity/module.cpp @@ -0,0 +1,47 @@ +/* + * This file is part of FGGL. + * + * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with FGGL. + * If not, see <https://www.gnu.org/licenses/>. + */ + +// +// stateless component factories that are probably safe +// + +#include "fggl/entity/module.hpp" +#include "fggl/math/types.hpp" + +namespace fggl::entity { + + void make_transform(const entity::ComponentSpec& spec, EntityManager& manager, const entity::EntityID entity){ + manager.add<math::Transform>(entity); + } + + void install_component_factories(entity::EntityFactory* factory) { + factory->bind(math::Transform::guid, make_transform); + } + + bool entity_svc_factory(modules::ModuleService service, modules::Services& services) { + if ( service == EntityFactory::service) { + auto* factory = services.create<EntityFactory>(); + install_component_factories(factory); + + // we are responsible for prefabs... + auto* assetLoader = services.get<assets::Loader>(); + assetLoader->setFactory(PROTOTYPE_ASSET, [factory](const assets::AssetGUID& a, assets::AssetData b) { + return load_prototype(factory, a, b); }, assets::LoadType::PATH ); + + return true; + } + return false; + } + const modules::ServiceFactory ECS::factory = entity_svc_factory; +} \ No newline at end of file diff --git a/fggl/gfx/ogl/renderer.cpp b/fggl/gfx/ogl/renderer.cpp index 8b45eedef6824d58c00d9da781bb1fde298409e1..722a232f9c9c935291ec99a6c82c4a449aabb903 100644 --- a/fggl/gfx/ogl/renderer.cpp +++ b/fggl/gfx/ogl/renderer.cpp @@ -194,24 +194,24 @@ namespace fggl::gfx { m_canvasRenderer->render(m_canvasPipeline, paint); } - void OpenGL4Backend::drawScene(ecs3::World &world) { + void OpenGL4Backend::drawScene(entity::EntityManager &world) { if ( m_modelRenderer ) { m_modelRenderer->render(world); } if ( m_debugRenderer ) { - auto cameras = world.findMatching<gfx::Camera>(); + auto cameras = world.find<gfx::Camera>(); if ( cameras.empty() ) { return; } auto cameraEnt = cameras.front(); - auto* const camTransform = world.get<math::Transform>(cameraEnt); - auto* const camComp = world.get<gfx::Camera>(cameraEnt); + const auto& camTransform = world.get<math::Transform>(cameraEnt); + const auto& camComp = world.get<gfx::Camera>(cameraEnt); - const math::mat4 projectionMatrix = glm::perspective(camComp->fov, camComp->aspectRatio, camComp->nearPlane, camComp->farPlane); - const math::mat4 viewMatrix = glm::lookAt( camTransform->origin(), camComp->target, camTransform->up() ); + const math::mat4 projectionMatrix = glm::perspective(camComp.fov, camComp.aspectRatio, camComp.nearPlane, camComp.farPlane); + const math::mat4 viewMatrix = glm::lookAt( camTransform.origin(), camComp.target, camTransform.up() ); m_debugRenderer->mvpMatrix = projectionMatrix * viewMatrix; dd::flush(); @@ -222,79 +222,4 @@ namespace fggl::gfx { glViewport(0, 0, width, height); } - // TODO(webpigeon): this shouldn't be hard-coded - constexpr glm::vec3 DEFAULT_LIGHTPOS = glm::vec3(20.0F, 20.0F, 15.0F); - - void GlMeshRenderer::render(fggl::ecs3::World &ecs, ecs3::entity_t camera, float dt) { - if (camera == ecs::NULL_ENTITY) { - debug::warning("tried to render a scene, but no camera exists!"); - return; - } - - auto entities = ecs.findMatching<GlRenderToken>(); - if (entities.empty()) { - debug::warning("asked to render, but no entities are renderable"); - return; - } - - total += dt; - - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - - glEnable(GL_DEPTH_TEST); - - // camera logic - auto *const camTransform = ecs.get<math::Transform>(camera); - auto *const camComp = ecs.get<gfx::Camera>(camera); - glm::mat4 proj = glm::perspective(camComp->fov, camComp->aspectRatio, - camComp->nearPlane, camComp->farPlane); - glm::mat4 view = - glm::lookAt(camTransform->origin(), camComp->target, camTransform->up()); - - // lighting - glm::vec3 lightPos = DEFAULT_LIGHTPOS; - - // TODO(webpigeon): better performance if grouped by vao first - // TODO(webpigeon): the nvidia performance presentation said I shouldn't use uniforms for large data - for (auto &entity : entities) { - const auto &transform = ecs.get<fggl::math::Transform>(entity); - const auto &mesh = ecs.get<GlRenderToken>(entity); - - glm::mat4 model = transform->model(); - // model = glm::rotate(model, glm::radians(total/2048.0f * 360.0f), - //glm::vec3(0.0f,1.0f,0.0f)); - - auto shader = mesh->pipeline; - glUseProgram(shader); - - glUniformMatrix4fv(glGetUniformLocation(shader, "model"), 1, GL_FALSE, - glm::value_ptr(model)); - glUniformMatrix4fv(glGetUniformLocation(shader, "view"), 1, GL_FALSE, - glm::value_ptr(view)); - glUniformMatrix4fv(glGetUniformLocation(shader, "projection"), 1, GL_FALSE, - glm::value_ptr(proj)); - - // lighting - GLint lightID = glGetUniformLocation(shader, "lightPos"); - if (lightID != -1) { - glUniform3fv(lightID, 1, glm::value_ptr(lightPos)); - } - - glBindVertexArray(mesh->vao); - - if (mesh->renderType == GlRenderType::triangle_strip) { - glEnable(GL_PRIMITIVE_RESTART); - glPrimitiveRestartIndex(mesh->restartVertex); - } - glDrawElements(mesh->renderType, mesh->idxSize, GL_UNSIGNED_INT, - reinterpret_cast<void *>(mesh->idxOffset)); - if (mesh->renderType == GlRenderType::triangle_strip) { - glDisable(GL_PRIMITIVE_RESTART); - } - } - - glBindVertexArray(0); - } - } // namespace fggl::gfx diff --git a/fggl/gfx/ogl4/CMakeLists.txt b/fggl/gfx/ogl4/CMakeLists.txt index 3b0954f68c4e5684f794b4842b8e6cdac7efbd59..93a61b15202c27edb630dcb029c154b9ba35a6fb 100644 --- a/fggl/gfx/ogl4/CMakeLists.txt +++ b/fggl/gfx/ogl4/CMakeLists.txt @@ -2,7 +2,9 @@ # Sources target_sources(fggl PRIVATE + setup.cpp canvas.cpp models.cpp debug.cpp + module.cpp ) diff --git a/fggl/gfx/ogl4/models.cpp b/fggl/gfx/ogl4/models.cpp index 62b3e0d952f6b7faa18395e45c7d26c0f8be6d94..bfed052ff445c5e5e53a5e0ebc2fe6fc721e1134 100644 --- a/fggl/gfx/ogl4/models.cpp +++ b/fggl/gfx/ogl4/models.cpp @@ -50,74 +50,74 @@ namespace fggl::gfx::ogl4 { return elementBuffer; } - static void setupComponent(StaticModel* modelComp, std::shared_ptr<ogl::Shader>& shader, data::Mesh& mesh) { + static void setupComponent(StaticModel& modelComp, std::shared_ptr<ogl::Shader>& shader, data::Mesh& mesh) { auto vao = std::make_shared< ogl::VertexArray >(); auto meshBuffer = setupArrayBuffer(vao, mesh.vertexList()); auto elementBuffer = setupIndexBuffer(vao, mesh.indexList()); // set up the element attributes - modelComp->vao = vao; - modelComp->vertexData = meshBuffer; - modelComp->elements = elementBuffer; - modelComp->pipeline = shader; - modelComp->elementCount = mesh.indexCount(); - modelComp->drawType = ogl::Primative::TRIANGLE; + modelComp.vao = vao; + modelComp.vertexData = meshBuffer; + modelComp.elements = elementBuffer; + modelComp.pipeline = shader; + modelComp.elementCount = mesh.indexCount(); + modelComp.drawType = ogl::Primative::TRIANGLE; } - void StaticModelRenderer::resolveModels(ecs3::World &world) { + void StaticModelRenderer::resolveModels(entity::EntityManager &world) { // FIXME: this needs something reactive or performance will suck. - auto renderables = world.findMatching<data::StaticMesh>(); - for (auto& renderable : renderables){ - auto currModel = world.tryGet<StaticModel>( renderable ); + auto renderables = world.find<data::StaticMesh>(); + for (const auto& renderable : renderables){ + auto* currModel = world.tryGet<StaticModel>( renderable ); if ( currModel != nullptr ){ continue; } - auto* meshComp = world.get<data::StaticMesh>(renderable); - auto* modelComp = world.add<StaticModel>(renderable); + auto& meshComp = world.get<data::StaticMesh>(renderable); + auto& modelComp = world.add<StaticModel>(renderable); auto shader = m_phong; try { - shader = std::make_shared<ogl::Shader>( m_shaders->get( meshComp->pipeline ) ); + shader = std::make_shared<ogl::Shader>( m_shaders->get( meshComp.pipeline ) ); } catch ( std::out_of_range& e) { - debug::log(debug::Level::warning, "Could not find shader: {}", meshComp->pipeline); + debug::log(debug::Level::warning, "Could not find shader: {}", meshComp.pipeline); } - setupComponent(modelComp, shader, meshComp->mesh); + setupComponent(modelComp, shader, meshComp.mesh); // no active model, we need to resolve/load one. - spdlog::info("looks like {} needs a static mesh", renderable); + //spdlog::info("looks like {} needs a static mesh", renderable); } // terrain - auto terrain = world.findMatching<data::HeightMap>(); + auto terrain = world.find<data::HeightMap>(); for (auto& renderable : terrain){ auto currModel = world.tryGet<StaticModel>( renderable ); if ( currModel != nullptr ){ continue; } - auto* heightmap = world.get<data::HeightMap>(renderable); + auto& heightmap = world.get<data::HeightMap>(renderable); data::Mesh heightMapMesh{}; data::generateHeightMesh(heightmap, heightMapMesh); - auto* modelComp = world.add<StaticModel>(renderable); + auto& modelComp = world.add<StaticModel>(renderable); setupComponent(modelComp, m_phong, heightMapMesh); // we know this is a triangle strip with a restart vertex... // FIXME the model should be telling us this... - modelComp->drawType = ogl::Primative::TRIANGLE_STRIP; - modelComp->restartIndex = heightMapMesh.restartVertex; + modelComp.drawType = ogl::Primative::TRIANGLE_STRIP; + modelComp.restartIndex = heightMapMesh.restartVertex; // no active model, we need to resolve/load one. - spdlog::info("looks like heightmap, {} needs a static mesh", renderable); + //spdlog::info("looks like heightmap, {} needs a static mesh", renderable); } } - void StaticModelRenderer::renderModelsForward(const ecs3::World &world) { + void StaticModelRenderer::renderModelsForward(const entity::EntityManager &world) { // fetch cameras we will need to render with - auto cameras = world.findMatching<gfx::Camera>(); + auto cameras = world.find<gfx::Camera>(); // if there are no cameras, we can't do anything... if ( cameras.size() == 0) { @@ -137,29 +137,29 @@ namespace fggl::gfx::ogl4 { glEnable( GL_DEPTH_TEST ); // set-up camera matrices - auto* const camTransform = world.get<math::Transform>(cameraEnt); - auto* const camComp = world.get<gfx::Camera>(cameraEnt); + const auto& camTransform = world.get<math::Transform>(cameraEnt); + const auto& camComp = world.get<gfx::Camera>(cameraEnt); - const math::mat4 projectionMatrix = glm::perspective(camComp->fov, camComp->aspectRatio, camComp->nearPlane, camComp->farPlane); - const math::mat4 viewMatrix = glm::lookAt( camTransform->origin(), camComp->target, camTransform->up() ); + const math::mat4 projectionMatrix = glm::perspective(camComp.fov, camComp.aspectRatio, camComp.nearPlane, camComp.farPlane); + const math::mat4 viewMatrix = glm::lookAt( camTransform.origin(), camComp.target, camTransform.up() ); // TODO lighting needs to not be this... math::vec3 lightPos{0.0f, 10.0f, 0.0f}; std::shared_ptr<ogl::Shader> shader = nullptr; - auto renderables = world.findMatching<StaticModel>(); + auto renderables = world.find<StaticModel>(); for ( const auto& entity : renderables ){ // ensure that the model pipeline actually exists... - StaticModel* model = world.get<StaticModel>(entity); - if ( model->pipeline == nullptr ) { + const StaticModel& model = world.get<StaticModel>(entity); + if ( model.pipeline == nullptr ) { spdlog::warn("shader was null, aborting render"); continue; } // check if we switched shaders - if ( shader != model->pipeline ) { + if ( shader != model.pipeline ) { // new shader - need to re-send the view and projection matrices - shader = model->pipeline; + shader = model.pipeline; shader->use(); if ( shader->hasUniform("projection") ) { shader->setUniformMtx(shader->uniform("view"), viewMatrix); @@ -168,14 +168,14 @@ namespace fggl::gfx::ogl4 { } // set model transform - auto* transform = world.get<math::Transform>(entity); + const auto& transform = world.get<math::Transform>(entity); if (shader->hasUniform("model")) { - shader->setUniformMtx(shader->uniform("model"), transform->model()); + shader->setUniformMtx(shader->uniform("model"), transform.model()); } else { - shader->setUniformMtx(shader->uniform("MVPMatrix"), projectionMatrix * viewMatrix * transform->model()); - shader->setUniformMtx(shader->uniform("MVMatrix"), viewMatrix * transform->model()); + shader->setUniformMtx(shader->uniform("MVPMatrix"), projectionMatrix * viewMatrix * transform.model()); + shader->setUniformMtx(shader->uniform("MVMatrix"), viewMatrix * transform.model()); - auto normalMatrix = glm::mat3(glm::transpose(inverse(transform->model()))); + auto normalMatrix = glm::mat3(glm::transpose(inverse(transform.model()))); shader->setUniformMtx(shader->uniform("NormalMatrix"), normalMatrix); } @@ -195,14 +195,14 @@ namespace fggl::gfx::ogl4 { if (!local) { lightPos = glm::normalize(lightPos); - auto viewDir = glm::normalize(camTransform->origin() - transform->origin()); + auto viewDir = glm::normalize(camTransform.origin() - transform.origin()); auto halfVector = glm::normalize(lightPos + viewDir); shader->setUniformF(shader->uniform("lights[0].halfVector"), halfVector ); shader->setUniformF(shader->uniform("EyeDirection"), viewDir); shader->setUniformF(shader->uniform("lights[0].position"), lightPos); } else { - auto camModelView = (viewMatrix * camTransform->model() * math::vec4(0.0f, 0.0f, 0.0f, 1.0f)); - auto modelModelView = (viewMatrix * transform->model() * math::vec4(0.0f, 0.0f, 0.0f, 1.0f)); + auto camModelView = (viewMatrix * camTransform.model() * math::vec4(0.0f, 0.0f, 0.0f, 1.0f)); + auto modelModelView = (viewMatrix * transform.model() * math::vec4(0.0f, 0.0f, 0.0f, 1.0f)); math::vec3 viewDir = glm::normalize(camModelView - modelModelView); shader->setUniformF(shader->uniform("EyeDirection"), viewDir); @@ -214,40 +214,37 @@ namespace fggl::gfx::ogl4 { } // material detection with fallback - auto* material = &gfx::DEFAULT_MATERIAL; - if ( world.has<PhongMaterial>(entity) ) { - material = world.get<PhongMaterial>(entity); - } + const auto& material = world.get<PhongMaterial>(entity); if ( shader->hasUniform("material.ambient") ) { - shader->setUniformF(shader->uniform("material.ambient"), material->ambient); - shader->setUniformF(shader->uniform("material.diffuse"), material->diffuse); - shader->setUniformF(shader->uniform("material.specular"), material->specular); - shader->setUniformF(shader->uniform("material.shininess"), material->shininess); + shader->setUniformF(shader->uniform("material.ambient"), material.ambient); + shader->setUniformF(shader->uniform("material.diffuse"), material.diffuse); + shader->setUniformF(shader->uniform("material.specular"), material.specular); + shader->setUniformF(shader->uniform("material.shininess"), material.shininess); } else { - shader->setUniformF(shader->uniform("materials[0].emission"), material->emission); - shader->setUniformF(shader->uniform("materials[0].ambient"), material->ambient); - shader->setUniformF(shader->uniform("materials[0].diffuse"), material->diffuse); - shader->setUniformF(shader->uniform("materials[0].specular"), material->specular); - shader->setUniformF(shader->uniform("materials[0].shininess"), material->shininess); + shader->setUniformF(shader->uniform("materials[0].emission"), material.emission); + shader->setUniformF(shader->uniform("materials[0].ambient"), material.ambient); + shader->setUniformF(shader->uniform("materials[0].diffuse"), material.diffuse); + shader->setUniformF(shader->uniform("materials[0].specular"), material.specular); + shader->setUniformF(shader->uniform("materials[0].shininess"), material.shininess); } if ( shader->hasUniform("lightPos")) { shader->setUniformF( shader->uniform("lightPos"), lightPos); } - auto vao = model->vao; + auto vao = model.vao; vao->bind(); - model->vertexData->bind(); + model.vertexData->bind(); - if ( model->restartIndex != NO_RESTART_IDX) { + if ( model.restartIndex != NO_RESTART_IDX) { glEnable(GL_PRIMITIVE_RESTART); - glPrimitiveRestartIndex(model->restartIndex); + glPrimitiveRestartIndex(model.restartIndex); } - auto elements = model->elements.get(); - vao->drawElements( *elements, model->drawType, model->elementCount); - if ( model->restartIndex != NO_RESTART_IDX) { + auto elements = model.elements.get(); + vao->drawElements( *elements, model.drawType, model.elementCount); + if ( model.restartIndex != NO_RESTART_IDX) { glDisable(GL_PRIMITIVE_RESTART); } } diff --git a/fggl/gfx/ogl4/module.cpp b/fggl/gfx/ogl4/module.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aad07e8c91bb5672d189f400b6b54c8b718fabb9 --- /dev/null +++ b/fggl/gfx/ogl4/module.cpp @@ -0,0 +1,103 @@ +/* + * This file is part of FGGL. + * + * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with FGGL. + * If not, see <https://www.gnu.org/licenses/>. + */ + +// +// Created by webpigeon on 24/07/22. +// + +#include "fggl/gfx/ogl4/module.hpp" +#include "fggl/gfx/phong.hpp" +#include "fggl/data/procedural.hpp" + +#include <string> + +namespace fggl::gfx { + + constexpr int DEFAULT_STACKS = 16; + constexpr int DEFAULT_SLICES = 16; + constexpr const char* SHAPE_SPHERE{"sphere"}; + constexpr const char* SHAPE_BOX{"box"}; + + static void process_shape(const YAML::Node& node, data::Mesh& mesh) { + auto transform = data::OFFSET_NONE; + if ( node["offset"] ) { + auto offset = node["offset"].as<math::vec3>(); + transform = glm::translate(transform, offset); + } + + if ( node["scale"] ) { + auto offset = node["scale"].as<math::vec3>(); + transform = glm::scale(transform, offset); + } + + // now the shape itself + auto type = node["type"].as<std::string>(); + if ( type == SHAPE_BOX ) { + data::make_cube(mesh, transform); + } else if ( type == SHAPE_SPHERE ) { + int stacks = node["stacks"].as<uint32_t>(DEFAULT_STACKS); + int slices = node["slices"].as<uint32_t>(DEFAULT_SLICES); + data::make_sphere(mesh, transform, stacks, slices); + } else { + debug::log(debug::Level::warning, "unknown shape type requested: {}", type); + } + } + + void attach_mesh(const entity::ComponentSpec& spec, entity::EntityManager& manager, const entity::EntityID& id) { + auto& meshComp = manager.add<data::StaticMesh>(id); + meshComp.pipeline = spec.get<std::string>("pipeline", ""); + + if ( spec.has("shape") ) { + // procedural mesh + data::Mesh mesh; + if ( spec.config["shape"].IsSequence() ) { + for( const auto& node : spec.config["shape"] ) { + process_shape(node, mesh); + } + } else { + process_shape(spec.config["shape"], mesh); + } + mesh.removeDups(); + meshComp.mesh = mesh; + } + } + + void attach_material(const entity::ComponentSpec& spec, entity::EntityManager& manager, const entity::EntityID& id) { + auto& mat = manager.add<gfx::PhongMaterial>(id); + } + + void attach_light(const entity::ComponentSpec& spec, entity::EntityManager& manager, const entity::EntityID& id) { + auto& light = manager.add<gfx::Light>(id); + } + + bool ogl4_factory(modules::ModuleService service, modules::Services& services) { + if (service == WindowGraphics::service) { + // setup the thing responsible for graphics + auto* storage = services.get<data::Storage>(); + auto* fontLibrary = services.get<gui::FontLibrary>(); + services.bind<WindowGraphics, ogl4::WindowGraphics>(storage, fontLibrary); + + // register as responsible for creating rendering components + auto* entityFactory = services.get<entity::EntityFactory>(); + entityFactory->bind(data::StaticMesh::guid, attach_mesh); + entityFactory->bind(gfx::PhongMaterial::guid, attach_material); + entityFactory->bind(gfx::Light::guid, attach_light); + + return true; + } + return false; + } + const modules::ServiceFactory OpenGL4::factory = ogl4_factory; + +} // namespace fggl::gfX::ogl4 \ No newline at end of file diff --git a/fggl/ecs3/prototype/world.cpp b/fggl/gfx/ogl4/setup.cpp similarity index 75% rename from fggl/ecs3/prototype/world.cpp rename to fggl/gfx/ogl4/setup.cpp index 4b7c98674e6493991a7ba047114cea8120350018..cf1a07e947018b38707bec3c250507a7bbd3d3bf 100644 --- a/fggl/ecs3/prototype/world.cpp +++ b/fggl/gfx/ogl4/setup.cpp @@ -13,7 +13,15 @@ */ // -// Created by webpigeon on 23/10/2021. +// Created by webpigeon on 24/07/22. // -#include <fggl/ecs3/prototype/world.hpp> +#include "fggl/gfx/ogl4/setup.hpp" + +namespace fggl::gfx::ogl4 { + + Graphics *WindowGraphics::create(display::Window &window) { + return new OpenGL4Backend(m_storage, m_fonts); + } + +} \ No newline at end of file diff --git a/fggl/input/camera_input.cpp b/fggl/input/camera_input.cpp index e0b04140ad97d30ec5ff7975fdcdfd6466d35fb9..6c6dbeefba5ac9ed8b4780487b995bc1c02953c5 100644 --- a/fggl/input/camera_input.cpp +++ b/fggl/input/camera_input.cpp @@ -16,7 +16,6 @@ // Created by webpigeon on 20/11/2021. // -#include <fggl/ecs3/ecs.hpp> #include <fggl/input/input.hpp> #include <fggl/gfx/camera.hpp> @@ -24,15 +23,15 @@ namespace fggl::input { - void process_arcball(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam) { + void process_arcball(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) { // see https://asliceofrendering.com/camera/2019/11/30/ArcballCamera/ - auto *camTransform = ecs.get<fggl::math::Transform>(cam); - auto *camComp = ecs.get<fggl::gfx::Camera>(cam); + auto& camTransform = ecs.get<fggl::math::Transform>(cam); + auto& camComp = ecs.get<fggl::gfx::Camera>(cam); auto &mouse = input.mouse; - glm::vec4 position(camTransform->origin(), 1.0f); - glm::vec4 pivot(camComp->target, 1.0f); - glm::mat4 view = glm::lookAt(camTransform->origin(), camComp->target, camTransform->up()); + glm::vec4 position(camTransform.origin(), 1.0f); + glm::vec4 pivot(camComp.target, 1.0f); + glm::mat4 view = glm::lookAt(camTransform.origin(), camComp.target, camTransform.up()); glm::vec3 viewDir = -glm::transpose(view)[2]; glm::vec3 rightDir = glm::transpose(view)[0]; @@ -51,52 +50,52 @@ namespace fggl::input { rotationMatrixY = glm::rotate(rotationMatrixY, yAngle, rightDir); glm::vec3 finalPos = (rotationMatrixY * (position - pivot)) + pivot; - camTransform->origin(finalPos); + camTransform.origin(finalPos); } - void process_scroll(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam, float minZoom, float maxZoom){ - auto* camTransform = ecs.get<fggl::math::Transform>(cam); - auto* camComp = ecs.get<fggl::gfx::Camera>(cam); + void process_scroll(entity::EntityManager &ecs, const Input &input, entity::EntityID cam, float minZoom, float maxZoom){ + auto& camTransform = ecs.get<fggl::math::Transform>(cam); + auto& camComp = ecs.get<fggl::gfx::Camera>(cam); - const glm::vec3 dir = ( camTransform->origin() - camComp->target ); + const glm::vec3 dir = ( camTransform.origin() - camComp.target ); const glm::vec3 forward = glm::normalize( dir ); glm::vec3 motion(0.0F); float delta = input.mouse.axis( fggl::input::MouseAxis::SCROLL_Y ); if ( (glm::length( dir ) < maxZoom && delta < 0.0f) || (glm::length( dir ) > minZoom && delta > 0.0f) ) { motion -= (forward * delta); - camTransform->origin(camTransform->origin() + motion); + camTransform.origin(camTransform.origin() + motion); } } - void process_freecam(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam) { + void process_freecam(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) { float rotationValue = 0.0f; glm::vec3 translation(0.0f); auto &keyboard = input.keyboard; - auto* settings = ecs.get<FreeCamKeys>(cam); + auto& settings = ecs.get<FreeCamKeys>(cam); // calculate rotation (user input) - if (keyboard.down(settings->rotate_cw)) { + if (keyboard.down(settings.rotate_cw)) { rotationValue = ROT_SPEED; - } else if (keyboard.down(settings->rotate_ccw)) { + } else if (keyboard.down(settings.rotate_ccw)) { rotationValue = -ROT_SPEED; } // calculate movement (user input) - if (keyboard.down(settings->forward)) { + if (keyboard.down(settings.forward)) { translation -= fggl::math::RIGHT; } - if (keyboard.down(settings->backward)) { + if (keyboard.down(settings.backward)) { translation += fggl::math::RIGHT; } - if (keyboard.down(settings->right)) { + if (keyboard.down(settings.right)) { translation += fggl::math::FORWARD; } - if (keyboard.down(settings->left)) { + if (keyboard.down(settings.left)) { translation -= fggl::math::FORWARD; } @@ -104,8 +103,8 @@ namespace fggl::input { auto camTransform = ecs.get<fggl::math::Transform>(cam); auto camComp = ecs.get<fggl::gfx::Camera>(cam); - glm::vec4 position(camTransform->origin(), 1.0f); - glm::vec4 pivot(camComp->target, 1.0f); + glm::vec4 position(camTransform.origin(), 1.0f); + glm::vec4 pivot(camComp.target, 1.0f); // apply movement if (translation != glm::vec3(0.0f)) { @@ -126,11 +125,11 @@ namespace fggl::input { position = (rotation * (position - pivot)) + pivot; } - camTransform->origin(position); - camComp->target = pivot; + camTransform.origin(position); + camComp.target = pivot; } - void process_edgescroll(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam) { + void process_edgescroll(entity::EntityManager &ecs, const Input &input, entity::EntityID cam) { glm::vec3 translation(0.0f); auto &mouse = input.mouse; @@ -153,11 +152,11 @@ namespace fggl::input { } // apply rotation/movement - auto camTransform = ecs.get<fggl::math::Transform>(cam); - auto camComp = ecs.get<fggl::gfx::Camera>(cam); + auto& camTransform = ecs.get<fggl::math::Transform>(cam); + auto& camComp = ecs.get<fggl::gfx::Camera>(cam); - glm::vec4 position(camTransform->origin(), 1.0f); - glm::vec4 pivot(camComp->target, 1.0f); + glm::vec4 position(camTransform.origin(), 1.0f); + glm::vec4 pivot(camComp.target, 1.0f); // apply movement if (translation != glm::vec3(0.0f)) { @@ -173,7 +172,7 @@ namespace fggl::input { } // move camera - camTransform->origin(position); - camComp->target = pivot; + camTransform.origin(position); + camComp.target = pivot; } } diff --git a/fggl/platform/linux/paths.cpp b/fggl/platform/linux/paths.cpp index 92f1a1a5f31f96a888b5f9401ab5a19f535e987c..d53cc7b7096bd1c3af7aa1eec6e365ebfcd8034e 100644 --- a/fggl/platform/linux/paths.cpp +++ b/fggl/platform/linux/paths.cpp @@ -83,7 +83,7 @@ namespace fggl::platform { // check system paths for ( const auto& path : paths.dataDirs ) { auto fullPath = path / relPath; - debug::log(debug::Level::warning, "Checking data path: {}, {}", fullPath.c_str(), std::filesystem::exists(fullPath)); + debug::log(debug::Level::debug, "Checking data path: {}, {}", fullPath.c_str(), std::filesystem::exists(fullPath)); if ( std::filesystem::exists(fullPath) ) { return fullPath; } @@ -92,6 +92,7 @@ namespace fggl::platform { // if debug mode, try CWD as well. auto debugPath = std::filesystem::current_path() / "data" / relPath; if ( std::filesystem::exists(debugPath) ) { + debug::log(debug::Level::debug, "Checking debug path: {}, {}", debugPath.c_str(), std::filesystem::exists(debugPath)); return debugPath; } diff --git a/fggl/scenes/game.cpp b/fggl/scenes/game.cpp index f5f7746f02758e7893dad62f3b0fef53b0705f55..5774f23a19d4bfd2e8d18f5ad89e8953cda28c11 100644 --- a/fggl/scenes/game.cpp +++ b/fggl/scenes/game.cpp @@ -32,7 +32,7 @@ namespace fggl::scenes { fggl::AppState::activate(); // setup the scene - m_world = std::make_unique<ecs3::World>(*m_owner.registry()); + m_world = std::make_unique<entity::EntityManager>(); #ifdef FGGL_MODULE_BULLET // FIXME this ties bullet to the game state - which shouldn't be the case @@ -59,7 +59,7 @@ namespace fggl::scenes { m_phys->step(); } - m_world->reapEntities(); + //m_world->reapEntities(); } void Game::render(fggl::gfx::Graphics &gfx) { diff --git a/fggl/util/guid.cpp b/fggl/util/guid.cpp index f2f908a080a174290ee6039470cc789fe88936c0..ee0025bc480968d0af66b5a9cb2cfad5ced3aff0 100644 --- a/fggl/util/guid.cpp +++ b/fggl/util/guid.cpp @@ -44,9 +44,9 @@ namespace fggl::util { auto tableValue = guidTable.find(guid); if (tableValue != guidTable.end()) { return tableValue->second; - } else { - return "FGGL_UNKNOWN_GUID"; } + // it's not in the table... + return "UNKNOWN_GUID("+std::to_string(guid.get())+")"; } } diff --git a/include/fggl/app.hpp b/include/fggl/app.hpp index e89b254a5d96296939c41b7342e1ad177a690756..b66846e69cafb2d996eaf0ce3e21e845aa7986c0 100644 --- a/include/fggl/app.hpp +++ b/include/fggl/app.hpp @@ -22,8 +22,6 @@ #include <memory> #include <unordered_map> -#include <fggl/ecs3/types.hpp> -#include "fggl/ecs3/module/module.hpp" #include "fggl/display/glfw/window.hpp" #include <fggl/gfx/paint.hpp> #include <fggl/util/states.hpp> @@ -108,12 +106,6 @@ namespace fggl { return m_states.put<T>(name, *this); } - template<typename T, typename... Args> - T &use(Args &&...args) { - auto ptr = m_modules->load<T>(args...); - return *ptr; - } - inline void change_state(const Identifer &name) { m_expectedScene = name; /*m_states.active().deactivate(); @@ -135,10 +127,6 @@ namespace fggl { } } - inline ecs3::TypeRegistry* registry() { - return m_types.get(); - } - inline bool running() const { return m_running; } @@ -149,8 +137,6 @@ namespace fggl { private: bool m_running; - std::unique_ptr<ecs3::TypeRegistry> m_types; - std::unique_ptr<ecs3::ModuleManager> m_modules; display::Window* m_window; AppMachine m_states; Identifer m_expectedScene; diff --git a/include/fggl/assets/loader.hpp b/include/fggl/assets/loader.hpp index be85581b13aa1d9da64a987f9edb86a613aef866..044b7a22a4c249075a9ed7e4e4ba376918f7e86c 100644 --- a/include/fggl/assets/loader.hpp +++ b/include/fggl/assets/loader.hpp @@ -45,7 +45,8 @@ namespace fggl::assets { class Loader { public: constexpr const static modules::ModuleService service = modules::make_service("fggl::assets::Loader"); - explicit inline Loader(data::Storage* storage) : m_storage(storage), m_parent(nullptr) {} + + explicit inline Loader(data::Storage* storage) : m_storage(storage) {} explicit Loader(Loader* parent, data::Storage* storage) : m_parent(parent), m_storage(storage) {}; // no move, no copy. @@ -65,16 +66,18 @@ namespace fggl::assets { m_requests.push(ResourceRequest{guid, type}); } - void load(const AssetGUID guid, const AssetType& type) { + void load(const AssetGUID& guid, const AssetType& type) { auto path = m_storage->resolvePath(data::StorageType::Data, guid); auto& config = m_factories.at(type); switch (config.second) { case LoadType::DIRECT: // TODO we load the data into main memory and give a pointer to it. + debug::log(debug::Level::error, "Tried to load direct asset - no one wrote that yet!"); break; case LoadType::STAGED: // TODO we load the data into temp memory and give a pointer to it. + debug::log(debug::Level::error, "Tried to load staged asset - no one wrote that yet!"); break; case LoadType::PATH: config.first(guid, AssetData(&path)); diff --git a/include/fggl/data/heightmap.hpp b/include/fggl/data/heightmap.hpp index 312659ef6d7557e7db855df5006c6ea2200ac16b..d541368fd48b88fc6fd0c778c5842aff78e33a81 100644 --- a/include/fggl/data/heightmap.hpp +++ b/include/fggl/data/heightmap.hpp @@ -54,7 +54,7 @@ namespace fggl::data { return x * zMax + z; } - void generateHeightMesh(const data::HeightMap *heights, data::Mesh &mesh); + void generateHeightMesh(const data::HeightMap& heights, data::Mesh &mesh); } #endif //FGGL_DATA_HEIGHTMAP_HPP diff --git a/include/fggl/data/model.hpp b/include/fggl/data/model.hpp index 3944a8af3151a3cb6c3953c8622421de6c619b05..900b1e698440695f958c88e3b251a0f050e58c55 100644 --- a/include/fggl/data/model.hpp +++ b/include/fggl/data/model.hpp @@ -37,7 +37,8 @@ namespace fggl::data { return { pos, ILLEGAL_NORMAL, - DEFAULT_COLOUR + DEFAULT_COLOUR, + {0.0F, 0.0F} }; } }; @@ -161,6 +162,8 @@ namespace fggl::data { struct StaticMesh { constexpr static const char name[] = "StaticMesh"; + constexpr static const util::GUID guid = util::make_guid("StaticMesh"); + data::Mesh mesh; std::string pipeline; diff --git a/include/fggl/ds/placeholder.hpp b/include/fggl/ds/placeholder.hpp index ab9d9f77baaf34cec86326871e52daf0919a6fac..a4623a19aa6eb40e99426466a82ac2a30b04873a 100644 --- a/include/fggl/ds/placeholder.hpp +++ b/include/fggl/ds/placeholder.hpp @@ -32,7 +32,7 @@ namespace fggl::ds { using WeakRef = std::size_t; constexpr static WeakRef BAD_INDEX = 0; - bool valid(WeakRef idx) const { + inline bool valid(WeakRef idx) const { return m_data.find(idx) != m_data.end(); } @@ -67,8 +67,6 @@ namespace fggl::ds { std::size_t m_nextIdx = 1; }; - template<typename T, std::size_t N> - using SlotMap = FakeSlotMap<T, N>; } // namespace fggl::ds diff --git a/fggl/ecs3/module/module.cpp b/include/fggl/ds/slot_map.hpp similarity index 70% rename from fggl/ecs3/module/module.cpp rename to include/fggl/ds/slot_map.hpp index 88cee8822b0186756e665e9b088ba6131a5a66d3..56443f50f412c8e34de149bf1df7efb24f92a10b 100644 --- a/fggl/ecs3/module/module.cpp +++ b/include/fggl/ds/slot_map.hpp @@ -13,15 +13,19 @@ */ // -// Created by webpigeon on 23/10/2021. +// Created by webpigeon on 23/07/22. // -#include "fggl/ecs3/module/module.hpp" +#ifndef FGGL_DS_SLOT_MAP_HPP +#define FGGL_DS_SLOT_MAP_HPP -namespace fggl::ecs3 { +#include "fggl/ds/placeholder.hpp" - // default empty implementions - void Module::onUpdate() {} - void Module::onFrameStart() {} - void Module::onFrameEnd() {} -} +namespace fggl::ds { + + template<typename T, std::size_t N> + using SlotMap = FakeSlotMap<T, N>; + +} // namespace fggl::ds + +#endif //FGGL_DS_SLOT_MAP_HPP diff --git a/include/fggl/ecs/component.hpp b/include/fggl/ecs/component.hpp deleted file mode 100644 index 46c382c1950db935f87afd141065d2e5d86a6b6f..0000000000000000000000000000000000000000 --- a/include/fggl/ecs/component.hpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * This file is part of FGGL. - * - * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with FGGL. - * If not, see <https://www.gnu.org/licenses/>. - */ - -#ifndef FGGL_ECS_COMPONENT_HPP -#define FGGL_ECS_COMPONENT_HPP - -#include "utility.hpp" -#include "fggl/debug/logging.hpp" - -#include <cassert> -#include <cstring> -#include <string> -#include <new> -#include <utility> -#include <iostream> - -#include <typeinfo> - -#include <vector> -#include <unordered_map> -#include "yaml-cpp/yaml.h" - -namespace fggl::ecs { - - template<typename T> - bool restore_config(T* comp, const YAML::Node& node); - - class ComponentBase { - public: - using data_t = unsigned char; - - virtual ~ComponentBase() {}; - - // in place - virtual void destroy(data_t *data) const = 0; - virtual void move(data_t *src, data_t *dest) const = 0; - virtual void bulkMove(data_t *src, data_t *dest, std::size_t count) = 0; - virtual void construct(data_t *data) const = 0; - - // virtual - virtual void *construct() const = 0; - virtual void *restore(const YAML::Node& config) const = 0; - - virtual void *copyConstruct(const void *src) = 0; - - virtual const char *name() const = 0; - virtual const component_type_t id() const = 0; - - virtual std::size_t size() const = 0; - }; - - template<class C> - class Component : public ComponentBase { - public: - virtual void destroy(data_t *data) const override { - C *location = std::launder(reinterpret_cast<C *>(data)); - location->~C(); - } - - virtual const char *name() const override { - return C::name; - } - - virtual const component_type_t id() const { - return Component<C>::typeID(); - } - - virtual void construct(unsigned char *data) const override { - new(data) C(); - } - - virtual void* restore(const YAML::Node& config) const override { - C* ptr = new C(); - bool restored = restore_config<C>(ptr, config); - if ( !restored ) { - debug::error("error restoring {}", C::name); - assert( false && "failed to restore configuration when loading type!" ); - } - return ptr; - } - - void *copyConstruct(const void *src) override { - const C *srcPtr = (C *) src; - return new C(*srcPtr); - } - - void *construct() const override { - return new C(); - } - - virtual void move(data_t *src, data_t *dest) const override { - assert(src != nullptr); - assert(dest != nullptr); - new(&dest[0]) C(std::move(*reinterpret_cast<C *>(src))); - } - - virtual void bulkMove(data_t *src, data_t *dest, std::size_t count) { - if (std::is_trivially_copyable<C>::value) { - std::memcpy(dest, src, count * size()); - } else { - unsigned char *srcPtr = src; - unsigned char *destPtr = dest; - - for (std::size_t i = 0; i < count; ++i) { - new(destPtr) C(std::move(*reinterpret_cast<C *>(srcPtr))); - srcPtr += sizeof(C); - destPtr += sizeof(C); - } - } - } - - virtual std::size_t size() const { - return sizeof(C); - } - - static component_type_t typeID() { - return TypeIdGenerator<ComponentBase>::GetNewID<C>(); - } - }; - -} - -#endif diff --git a/include/fggl/ecs/component_fwd.hpp b/include/fggl/ecs/component_fwd.hpp deleted file mode 100644 index 22c1dafb5414abc6d348065be6040b4695391bf0..0000000000000000000000000000000000000000 --- a/include/fggl/ecs/component_fwd.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * This file is part of FGGL. - * - * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with FGGL. - * If not, see <https://www.gnu.org/licenses/>. - */ - -// -// Created by webpigeon on 22/07/22. -// - -#ifndef FGGL_ECS_COMPONENT_FWD_HPP -#define FGGL_ECS_COMPONENT_FWD_HPP - -#include "fggl/ecs/component.hpp" - -#include "fggl/math/types.hpp" -#include "fggl/gfx/phong.hpp" -#include "fggl/data/model.hpp" -#include "fggl/phys/types.hpp" - -#ifdef __GNUC__ - #include <cxxabi.h> -#endif - -namespace fggl::ecs { - - template<typename T> - bool restore_config(T* comp, const YAML::Node& node) { - char* realName; - - #ifdef __GNUC__ - int status; - realName = abi::__cxa_demangle(typeid(T).name(), 0, 0, &status); - #endif - - debug::log(debug::Level::warning, "restore_config is not implemented for {}", realName); - return false; - } - - template<> - bool restore_config(math::Transform* comp, const YAML::Node& node); - - template<> - bool restore_config(gfx::PhongMaterial* comp, const YAML::Node& node); - - template<> - bool restore_config(data::StaticMesh* meshComp, const YAML::Node& node); - - template<> - bool restore_config(phys::RigidBody* body, const YAML::Node& node); - - template<> - bool restore_config(phys::CollisionCallbacks* callbacks, const YAML::Node& node); - - template<> - bool restore_config(phys::CollisionCache* cache, const YAML::Node& node); - -} // namespace fggl::ecs - -#endif //FGGL_ECS_COMPONENT_FWD_HPP diff --git a/include/fggl/ecs/ecs.hpp b/include/fggl/ecs/ecs.hpp deleted file mode 100644 index d7fce80f0a8dc94b930c03f767a3f2dd0fdb18c1..0000000000000000000000000000000000000000 --- a/include/fggl/ecs/ecs.hpp +++ /dev/null @@ -1,302 +0,0 @@ -/* - * This file is part of FGGL. - * - * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with FGGL. - * If not, see <https://www.gnu.org/licenses/>. - */ - -#ifndef FGGL_ECS_ECS_HPP -#define FGGL_ECS_ECS_HPP - -#include "utility.hpp" -#include "component.hpp" - -#include <iostream> -#include <algorithm> -#include <cassert> -#include <string> -#include <vector> -#include <unordered_map> - -namespace fggl::ecs { - using archToken_t = std::vector<component_type_t>; - - struct Archetype { - constexpr static unsigned int default_cap = 0; - const archToken_t type; - std::vector<ComponentBase::data_t *> data; - std::vector<std::size_t> dataSizes; - std::vector<entity_t> entities; - - Archetype(const archToken_t &type_a); - - inline archToken_t create(const component_type_t cid) const { - assert(!contains(cid)); - - // create the new type - auto newType = type; - newType.push_back(cid); - std::sort(newType.begin(), newType.end()); - return newType; - } - - inline bool contains(const component_type_t cid) const { - return (std::find(type.begin(), type.end(), cid) != type.end()); - } - }; - - class ECS { - struct Record { - Archetype *archetype; - std::size_t index; - }; - using componentmap_t = std::unordered_map<component_type_t, ComponentBase *>; - using entitymap_t = std::unordered_map<entity_t, Record>; - using archetype_t = std::vector<Archetype *>; - - public: - ECS(); - ~ECS(); - - entity_t getNewID(); - entity_t createEntity(); - void removeEntity(const entity_t eid); - - template<class C> - void registerComponent() { - component_type_t type = Component<C>::typeID(); - if (m_componentMap.find(type) != m_componentMap.end()) - return; - m_componentMap.emplace(type, new Component<C>); - } - - template<class C> - bool isComponentRegistered() { - component_type_t type = Component<C>::typeID(); - return (m_componentMap.find(type) != m_componentMap.end()); - } - - template<class C, typename... Args> - C *addComponent(const entity_t &id, Args &&... args) { - component_type_t type = Component<C>::typeID(); - assert(isComponentRegistered<C>()); - - Record &record = m_entityArchtypes[id]; - Archetype *oldArch = record.archetype; - C *newComp = nullptr; - Archetype *newArch = nullptr; - - if (!oldArch) { - archToken_t newID(1, type); - const ComponentBase *const newCompType = m_componentMap[type]; - - // fetch type - newArch = getArchetype(newID); - assert(newArch->type.size() == 1); - - // calculate if we have enouph space to allocate - std::size_t emplacementPos = ensureCapacity(newArch, 0, newCompType); - newComp = new(&newArch->data[0][emplacementPos])C(std::forward<Args>(args)...); - } else { - // check if the arch contains the component - if (oldArch->contains(type)) { - return nullptr; - } - - // create a new archetype with the component - auto newID = oldArch->create(type); - newArch = getArchetype(newID); - - // relocate the old data to the new archetype - for (std::size_t j = 0; j < newID.size(); ++j) { - const component_type_t compType = newID[j]; - const ComponentBase *const comp = m_componentMap.at(compType); - - - // TODO this seems a little suspect - surely we could allocate all blocks at once? - // if per component the arrays could become out of sync... - int newOffset = ensureCapacity(newArch, j, comp); - int oldIdx = getComponentIdx(oldArch, compType); - - if (oldIdx != -1) { - assert(oldArch->contains(compType)); - const std::size_t compSize = comp->size(); - const std::size_t oldOffset = record.index * compSize; - - comp->move(&oldArch->data[oldIdx][oldOffset], - &newArch->data[j][newOffset]); - comp->destroy(&oldArch->data[oldIdx][oldOffset]); - } else { - assert(!oldArch->contains(compType)); - newComp = new(&newArch->data[j][newOffset]) - C(std::forward<Args>(args)...); - } - } - - // ensure the old archetype is still contigious - const int lastEnt = oldArch->entities.size() - 1; - if (lastEnt != record.index) { - for (std::size_t i = 0; i < oldArch->type.size(); ++i) { - const component_type_t typeID = oldArch->type[i]; - const ComponentBase *const comp = m_componentMap[typeID]; - const std::size_t &compSize = comp->size(); - - // shift the empty record to the end of the list - std::size_t slotOffset = record.index * compSize; - std::size_t lastOffset = lastEnt * compSize; - - // if we're not the last entity, swap - if (slotOffset != lastOffset) { - comp->move(&oldArch->data[i][lastOffset], - &oldArch->data[i][slotOffset]); - comp->destroy(&oldArch->data[i][lastOffset]); - } - } - - // fix the position - oldArch->entities[record.index] = oldArch->entities[lastEnt]; - } - oldArch->entities.pop_back(); - } - - // register the new data with the new archetype - newArch->entities.push_back(id); - record.index = newArch->entities.size() - 1; - record.archetype = newArch; - return newComp; - } - - template<class C> - void removeComponent(const entity_t &entityId); - - template<class C> - bool hasComponent(const entity_t &entityId) const { - const component_type_t componentID = Component<C>::typeID(); - const auto *arch = m_entityArchtypes.at(entityId).archetype; - if (arch == nullptr) { - return false; - } - return (std::find(arch->type.begin(), arch->type.end(), componentID) != - arch->type.end()); - } - - template<class C> - C *getComponent(const entity_t &entityId) { - assert(hasComponent<C>(entityId)); - const auto type = Component<C>::typeID(); - - const ComponentBase *const newComp = m_componentMap[type]; - const auto record = m_entityArchtypes.at(entityId); - const auto *arch = record.archetype; - - // JWR: linear search... seems a little suspect, they're ordered after all - for (std::size_t i = 0; i < arch->type.size(); ++i) { - if (arch->type[i] == type) { - return reinterpret_cast<C *>(&(arch->data[i][record.index * newComp->size()])); - } - } - - return nullptr; - } - - template<class C> - const C *getComponent(const entity_t &entityId) const { - assert(hasComponent<C>(entityId)); - const auto type = Component<C>::typeID(); - - const ComponentBase *const newComp = m_componentMap.at(type); - const auto record = m_entityArchtypes.at(entityId); - const auto *arch = record.archetype; - - // JWR: linear search... seems a little suspect, they're ordered after all - for (std::size_t i = 0; i < arch->type.size(); ++i) { - if (arch->type[i] == type) { - return reinterpret_cast<C *>(&(arch->data[i][record.index * newComp->size()])); - } - } - - return nullptr; - } - - template<class... Cs> - std::vector<entity_t> getEntityWith() const { - // construct the key - archToken_t key; - (key.push_back(Component<Cs>::typeID()), ...); - - // entities - std::vector<entity_t> entities; - for (Archetype *arch : m_archetypes) { - if (std::includes(arch->type.begin(), arch->type.end(), key.begin(), key.end())) { - if (!arch->entities.empty()) { - entities.insert(entities.begin(), arch->entities.begin(), arch->entities.end()); - } - } - } - - return entities; - } - - private: - entitymap_t m_entityArchtypes; - archetype_t m_archetypes; - entity_t m_entityIDCounter; - componentmap_t m_componentMap; - Archetype *getArchetype(const archToken_t &id); - - inline std::string arch2str(Archetype *arch) { - std::string str; - for (const auto &type : arch->type) { - str += std::to_string(type); - } - return str; - } - - inline std::size_t ensureCapacity(Archetype *arch, const int idx, const ComponentBase *const comp) { - const std::size_t &compSize = comp->size(); - std::size_t currSize = arch->entities.size() * compSize; - std::size_t newSize = currSize + compSize; - - std::size_t cap = arch->dataSizes[idx]; - - if (newSize > arch->dataSizes[idx]) { - arch->dataSizes[idx] *= 2; - arch->dataSizes[idx] += compSize; - - // copy data over - unsigned char *newData = new unsigned char[arch->dataSizes[idx]]; - for (std::size_t e = 0; e < arch->entities.size(); ++e) { - const int offset = e * compSize; - comp->move(&arch->data[idx][offset], &newData[offset]); - comp->destroy(&arch->data[idx][offset]); - } - - // free the old data and swap the pointers - delete[] arch->data[idx]; - arch->data[idx] = newData; - } - - return currSize; - } - - inline int getComponentIdx(const Archetype *arch, const component_type_t goal) { - // JWR could do binary search for speedup - for (std::size_t i = 0; i < arch->type.size(); ++i) { - if (arch->type[i] == goal) - return i; - } - return -1; - } - - }; - -} - -#endif diff --git a/include/fggl/ecs/utility.hpp b/include/fggl/ecs/utility.hpp deleted file mode 100644 index ebcc051e6625a73c80d726308fb2531f74df781e..0000000000000000000000000000000000000000 --- a/include/fggl/ecs/utility.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of FGGL. - * - * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with FGGL. - * If not, see <https://www.gnu.org/licenses/>. - */ - -#ifndef FGGL_ECS_UTILITY_HPP -#define FGGL_ECS_UTILITY_HPP - -#include <cstdint> - -namespace fggl::ecs { - - using IDType = std::uint32_t; - using entity_t = IDType; - using component_type_t = IDType; - - constexpr IDType NULL_ENTITY = 0; - - template<class T> - class TypeIdGenerator { - private: - static IDType m_count; - public: - template<class U> - static const IDType GetNewID() { - static const IDType idCounter = m_count++; - return idCounter; - } - }; - - template<class T> IDType TypeIdGenerator<T>::m_count = 1; -} - -#endif diff --git a/include/fggl/ecs3/ecs.hpp b/include/fggl/ecs3/ecs.hpp deleted file mode 100644 index 5f681515d635740dc44e37e53ed4cac315ff3066..0000000000000000000000000000000000000000 --- a/include/fggl/ecs3/ecs.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is part of FGGL. - * - * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with FGGL. - * If not, see <https://www.gnu.org/licenses/>. - */ - -#ifndef FGGL_ECS3_ECS_HPP -#define FGGL_ECS3_ECS_HPP - -#include "fggl/ecs3/module/module.hpp" -#include <fggl/ecs3/prototype/world.hpp> -#include <fggl/math/types.hpp> - -namespace fggl::ecs3 { - - using World = prototype::World; - - class ecsTypes : public Module { - - public: - inline std::string name() const override { - return "ecs::core"; - } - - inline void onLoad(ModuleManager& manager, TypeRegistry& types) override { - } - }; - -} - -#endif \ No newline at end of file diff --git a/include/fggl/ecs3/fast/Container.hpp b/include/fggl/ecs3/fast/Container.hpp deleted file mode 100644 index 3516ab30637f1f22d6c2a7cafb1b0bc369bb1de5..0000000000000000000000000000000000000000 --- a/include/fggl/ecs3/fast/Container.hpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * This file is part of FGGL. - * - * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with FGGL. - * If not, see <https://www.gnu.org/licenses/>. - */ - -// -// Created by webpigeon on 23/10/2021. -// - -#ifndef FGGL_ECS3_FAST_CONTAINER_HPP -#define FGGL_ECS3_FAST_CONTAINER_HPP - -#include <cstdint> -#include <cstdarg> -#include <cassert> - -#include <fggl/ecs3/types.hpp> - -namespace fggl::ecs3 { - - - class Container { - public: - const RecordIdentifier m_identifier; - - Container(const TypeRegistry ®, RecordIdentifier id) : - m_identifier(id), - m_types(reg), - backingStore(nullptr), - m_size(0), - m_capacity(0) {}; - - ~Container() { - delete[] backingStore; - } - - std::size_t create(); - - void remove(std::size_t pos); - - std::size_t expand(Container &other, std::size_t otherPos, component_type_t newComp); - - void contract(Container &other, std::size_t otherPos); - - void ensure(std::size_t size); - - inline unsigned char *data_raw(component_type_t type) { - auto seek_id = m_identifier.idx(type); - - // you asked for something I don't contain... - if (seek_id == m_identifier.count) { - std::cerr << "asked for " << type << " from " << m_identifier << std::endl; - assert(seek_id != m_identifier.count); - return nullptr; - } - - // figure out the offset - return backingStore + offsets[seek_id]; - } - - template<typename T> - inline T *data() { - auto comp_id = Component<T>::typeID(); - return (T *) data_raw(comp_id); - } - - template<typename T> - T *set(std::size_t entity, T *compData) { - auto *comps = data<T>(); - auto entityPos = idx(entity); - - auto compMeta = m_types.template meta<T>(); - - unsigned char *usrPtr = (unsigned char *) &comps[entityPos]; - compMeta->destroy(usrPtr); - compMeta->move((unsigned char *) compData, usrPtr); - - return &comps[entityPos]; - } - - [[nodiscard]] - inline std::size_t size() const { - return m_size; - } - - inline std::size_t idx(entity_t entity) { - auto *entityData = data<EntityMeta>(); - for (std::size_t i = 0; i < m_size; i++) { - if (entityData[i].id == entity) { - return i; - } - } - return m_size; - } - - private: - const TypeRegistry &m_types; - unsigned char *backingStore; - std::size_t offsets[RecordIdentifier::MAX_COMPS]{}; - std::size_t m_size; - std::size_t m_capacity; - - void move(std::size_t newPos, Container &oldContainer, std::size_t oldPos); - }; - -} - -#endif //FGGL_ECS3_FAST_CONTAINER_HPP diff --git a/include/fggl/ecs3/fast/ecs.hpp b/include/fggl/ecs3/fast/ecs.hpp deleted file mode 100644 index 6161b6b26737a7ffdc1198bdad6f25241ee60b2e..0000000000000000000000000000000000000000 --- a/include/fggl/ecs3/fast/ecs.hpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * This file is part of FGGL. - * - * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with FGGL. - * If not, see <https://www.gnu.org/licenses/>. - */ - -#ifndef FGGL_ECS3_FAST_ECS_HPP -#define FGGL_ECS3_FAST_ECS_HPP - -#include <cstddef> -#include <cstdarg> -#include <cassert> -#include <cstring> - -#include <map> -#include <algorithm> -#include <iostream> -#include <type_traits> - -#include <fggl/ecs3/utils.hpp> -#include <fggl/ecs3/types.hpp> -#include <fggl/ecs3/fast/Container.hpp> - -namespace fggl::ecs3::fast { - - using entity_t = unsigned int; - constexpr entity_t NULL_ENTITY = 0; - - class World { - public: - explicit World(TypeRegistry ®) : m_registry(reg), m_last(NULL_ENTITY) {} - - entity_t create() { - auto next = m_last++; - - auto arch = make_id(1, Component<EntityMeta>::typeID()); - auto &container = getContainer(arch); - - m_entities[next] = container.m_identifier; - - auto pos = container.create(); - auto *entityMeta = container.data<EntityMeta>(); - entityMeta[pos].id = next; - - return next; - } - - void remove(entity_t entity) { - auto arch = m_entities.at(entity); - auto container = m_records.at(arch); - - auto entPos = container.idx(entity); - container.remove(entPos); - } - - inline Container &getContainer(RecordIdentifier &arch) { - try { - return m_records.at(arch); - } catch (std::out_of_range &e) { - auto v = m_records.emplace(std::pair<RecordIdentifier, Container>(arch, {m_registry, arch})); - return v.first->second; - } - } - - template<typename T> - T *add(const entity_t entity) { - auto currArch = m_entities.at(entity); - auto newArch = currArch.with<T>(); - m_entities[entity] = newArch; - - auto &oldContainer = m_records.at(currArch); - auto &newContainer = getContainer(newArch); - - auto oldPos = oldContainer.idx(entity); - - auto newPos = newContainer.expand(oldContainer, oldPos, Component<T>::typeID()); - auto *data = newContainer.template data<T>(); - return &data[newPos]; - } - - template<typename T> - T *set(const entity_t entity, T *record) { - auto currArch = m_entities.at(entity); - - // check we already have that component type... - if (currArch.idx(Component<T>::typeID()) == currArch.count) { - add<T>(entity); - currArch = m_entities.at(entity); - } - - auto &container = m_records.at(currArch); - auto pos = container.idx(entity); - return container.set<T>(pos, record); - } - - template<typename T> - T *get(entity_t entity) { - auto currArch = m_entities.at(entity); - auto pos = currArch.idx(entity); - auto &container = m_records.at(currArch); - - auto *data = container.template data<T>(); - return &data[pos]; - } - - template<typename T> - const T *get(entity_t entity) const { - auto currArch = m_entities.at(entity); - auto pos = currArch.idx(entity); - auto &container = m_records.at(currArch); - - auto *data = container.template data<T>(); - return &data[pos]; - } - - template<typename T> - void remove(entity_t entity) { - auto currArch = m_entities.at(entity); - auto newArch = currArch.without<T>(); - - auto &oldContainer = m_records[currArch]; - auto &newContainer = m_records[newArch]; - - auto oldPos = oldContainer.idx(entity); - auto newPos = newContainer.create(); - - m_records[newArch].contract(newPos, oldContainer, oldPos); - } - - template<typename... T> - std::vector<entity_t> findMatching() const { - return {}; - } - - private: - TypeRegistry &m_registry; - std::map<RecordIdentifier, Container> m_records; - std::map<entity_t, RecordIdentifier> m_entities; - entity_t m_last{}; - }; - -} - -#endif diff --git a/include/fggl/ecs3/module/module.hpp b/include/fggl/ecs3/module/module.hpp deleted file mode 100644 index 13300b7b27351623e4dcf1fad6c52c12210ddcda..0000000000000000000000000000000000000000 --- a/include/fggl/ecs3/module/module.hpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * This file is part of FGGL. - * - * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with FGGL. - * If not, see <https://www.gnu.org/licenses/>. - */ - -// -// Created by webpigeon on 23/10/2021. -// - -#ifndef FGGL_ECS3_MODULE_MODULE_HPP -#define FGGL_ECS3_MODULE_MODULE_HPP - -#include <string> -#include <map> -#include <memory> - -#include <fggl/ecs3/types.hpp> - -namespace fggl::ecs3 { - - class Module { - public: - virtual ~Module() = default; - - [[nodiscard]] virtual std::string name() const = 0; - - virtual void onLoad(ModuleManager &manager, TypeRegistry &tr) {}; - - virtual void onUpdate(); - virtual void onFrameStart(); - virtual void onFrameEnd(); - }; - - class ModuleManager { - public: - explicit ModuleManager(TypeRegistry &types) : m_types(types), m_modules() {} - - ~ModuleManager() = default; - - template<typename C, typename... Args> - std::shared_ptr<C> load(Args &... args) { - auto ptr = std::make_shared<C>(args...); - m_modules[ptr->name()] = ptr; - - ptr->onLoad(*this, m_types); - //spdlog::info("loaded ECS module: {}", ptr->name()); - return ptr; - } - - template<typename C> - void onAdd(const callback_t &cb) { - m_types.callbackAdd(Component<C>::typeID(), cb); - } - - void onUpdate() { - for (auto &[id, ptr] : m_modules) { - ptr->onUpdate(); - } - } - - void onFrameStart() { - for (auto &[id, ptr] : m_modules) { - ptr->onFrameStart(); - } - } - - void onFrameEnd() { - for (auto &[id, ptr] : m_modules) { - ptr->onFrameEnd(); - } - } - - private: - TypeRegistry &m_types; - std::map<std::string, std::shared_ptr<Module>> m_modules; - }; - -} - -#endif //FGGL_ECS3_MODULE_MODULE_HPP diff --git a/include/fggl/ecs3/prototype/loader.hpp b/include/fggl/ecs3/prototype/loader.hpp deleted file mode 100644 index 89fe334390cc0f9bed586a41907bc862126bf1ab..0000000000000000000000000000000000000000 --- a/include/fggl/ecs3/prototype/loader.hpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * This file is part of FGGL. - * - * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with FGGL. - * If not, see <https://www.gnu.org/licenses/>. - */ - -// -// Created by webpigeon on 04/06/22. -// - -#ifndef FGGL_ECS3_PROTOTYPE_LOADER_HPP -#define FGGL_ECS3_PROTOTYPE_LOADER_HPP - -#include "yaml-cpp/yaml.h" - -#include "fggl/math/types.hpp" -#include "fggl/phys/types.hpp" -#include "fggl/gfx/phong.hpp" - -#include "fggl/data/storage.hpp" -#include "fggl/data/model.hpp" -#include "fggl/data/procedural.hpp" - -#include "fggl/ecs3/ecs.hpp" -#include "fggl/ecs/component_fwd.hpp" - -namespace fggl::ecs3 { - - void load_prototype_node( ecs3::World& world, const YAML::Node& node); - void load_prototype_file( ecs3::World& world, data::Storage& storage, const std::string& name ); - -} // namespace fggl::ecs3 - -namespace YAML { - - template<> - struct convert<fggl::math::vec3> { - static Node encode(const fggl::math::vec3& rhs){ - Node node; - node.push_back(rhs.x); - node.push_back(rhs.y); - node.push_back(rhs.z); - return node; - } - - static bool decode(const Node& node, fggl::math::vec3& rhs) { - if ( !node.IsSequence() || node.size() != 3) { - return false; - } - - rhs.x = node[0].as<float>(); - rhs.y = node[1].as<float>(); - rhs.z = node[2].as<float>(); - return true; - } - }; - - template<> - struct convert<fggl::math::quat> { - static Node encode(const fggl::math::quat& rhs){ - Node node; - node.push_back(rhs.x); - node.push_back(rhs.y); - node.push_back(rhs.z); - node.push_back(rhs.w); - return node; - } - - static bool decode(const Node& node, fggl::math::quat& rhs) { - if ( !node.IsSequence() || node.size() != 3) { - return false; - } - - rhs.x = node[0].as<float>(); - rhs.y = node[1].as<float>(); - rhs.z = node[2].as<float>(); - rhs.w = node[3].as<float>(); - return true; - } - }; - - constexpr const char* TYPE_KINEMATIC = "kinematic"; - constexpr const char* TYPE_STATIC = "static"; - constexpr const char* TYPE_DYNAMIC = "dynamic"; - - template<> - struct convert<fggl::phys::BodyType> { - static Node encode(const fggl::phys::BodyType& rhs) { - switch (rhs) { - case fggl::phys::BodyType::KINEMATIC: - return Node(TYPE_KINEMATIC); - case fggl::phys::BodyType::STATIC: - return Node(TYPE_STATIC); - default: - case fggl::phys::BodyType::DYNAMIC: - return Node(TYPE_DYNAMIC); - } - } - - static bool decode(const Node& node, fggl::phys::BodyType& rhs) { - const auto value = node.as<std::string>(); - if ( value == TYPE_KINEMATIC ) { - rhs = fggl::phys::BodyType::KINEMATIC; - } else if ( value == TYPE_STATIC ) { - rhs = fggl::phys::BodyType::STATIC; - } else { - rhs = fggl::phys::BodyType::DYNAMIC; - } - return true; - } - }; - - -} - -namespace fggl::ecs { - - constexpr int DEFAULT_STACKS = 16; - constexpr int DEFAULT_SLICES = 16; - constexpr const char* SHAPE_SPHERE_VALUE{"sphere"}; - constexpr const char* SHAPE_BOX_VALUE{"box"}; - - // scene template specialisations -} - -#endif //FGGL_ECS3_PROTOTYPE_LOADER_HPP diff --git a/include/fggl/ecs3/prototype/world.hpp b/include/fggl/ecs3/prototype/world.hpp deleted file mode 100644 index 9e0a62a9fa44ab8de5e0f6cf38cc0665908af84c..0000000000000000000000000000000000000000 --- a/include/fggl/ecs3/prototype/world.hpp +++ /dev/null @@ -1,386 +0,0 @@ -/* - * This file is part of FGGL. - * - * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with FGGL. - * If not, see <https://www.gnu.org/licenses/>. - */ - -// -// Created by webpigeon on 23/10/2021. -// - -#ifndef FGGL_ECS3_PROTOTYPE_WORLD_HPP -#define FGGL_ECS3_PROTOTYPE_WORLD_HPP - -#include <map> -#include <functional> -#include <unordered_set> - -#include "fggl/ecs3/types.hpp" -#include "fggl/debug/logging.hpp" -#include <yaml-cpp/yaml.h> - -/** - * A component based implementation of a game world. - * - * This is not a true ECS but exposes a similar API to it for testing (with a lot less headaches). - */ -namespace fggl::ecs3::prototype { - - using EntityCallback = std::function<void(const entity_t)>; - - class Entity { - public: - bool m_abstract; - - explicit Entity(entity_t id) : m_abstract(false), m_id(id) {}; - - Entity(const Entity &entity) : m_id(entity.m_id), m_components(entity.m_components) { - //spdlog::info("entity created fro copy: {}", m_id); - } - - ~Entity() = default; - - template<typename C> - C *add() { - C *ptr = new C(); - m_components[Component<C>::typeID()] = ptr; - return ptr; - } - - void *add(std::shared_ptr<ComponentBase> t) { - void *ptr = t->construct(); - m_components[t->id()] = ptr; - return ptr; - } - - void* add(const std::shared_ptr<ComponentBase>& compMeta, const YAML::Node& config) { - void* ptr = compMeta->restore(config); - m_components[ compMeta->id() ] = ptr; - return ptr; - } - - template<typename C> - C *set(const C *ptr) { - C *newPtr = new C(*ptr); - m_components[Component<C>::typeID()] = newPtr; - return newPtr; - } - - void *set(const std::shared_ptr<ComponentBase> &t, const void *ptr) { - void *newPtr = t->copyConstruct(ptr); - m_components[t->id()] = newPtr; - return newPtr; - } - - template<typename C> - C *get() const { - void *ptr = m_components.at(Component<C>::typeID()); - return (C *) ptr; - } - - template<typename C> - void remove(){ - m_components.erase(Component<C>::typeID()); - } - - inline void *get(component_type_t t) { - return m_components.at(t); - } - - std::vector<component_type_t> getComponentIDs() { - std::vector<component_type_t> comps{}; - for (auto &[k, _] : m_components) { - comps.push_back(k); - } - return comps; - } - - bool hasComponents(std::vector<component_type_t> &Cs) const { - for (auto c : Cs) { - if (m_components.find(c) == m_components.end()) { - return false; - } - } - return true; - } - - private: - entity_t m_id; - std::map<component_type_t, void *> m_components; - }; - - class World { - public: - explicit World(TypeRegistry ®) : m_types(reg), m_next(1), m_entities() {}; - ~World() = default; - - entity_t create(bool abstract) { - auto nextID = m_next++; - m_entities.emplace(nextID, nextID); - - auto &entity = m_entities.at(nextID); - entity.m_abstract = abstract; - - // meta data - auto *meta = entity.add<ecs3::EntityMeta>(); - meta->id = nextID; - meta->abstract = abstract; - meta->typeName = ""; - - return nextID; - } - - inline entity_t createFromPrototype(const std::string& name) { - auto prototype = findPrototype(name); - if ( prototype == NULL_ENTITY) { - debug::log(debug::Level::warning, "attempted to create from non-existant prototype: {}", name); - return NULL_ENTITY; - } - return copy( prototype ); - } - - entity_t copy(entity_t prototype) { - auto clone = create(false); - - auto components = getComponents(prototype); - for (auto component : components) { - auto protoComp = get(prototype, component); - set(clone, component, protoComp); - } - - return clone; - } - - void addFromConfig(entity_t entity, component_type_t type, const YAML::Node& node) { - auto meta = m_types.meta(type); - - auto& entityObj = m_entities.at(entity); - entityObj.add(meta, node); - - m_types.fireAdd(this, entity, meta->id()); - } - - void createFromSpec(entity_t entity, const YAML::Node& compConfig) { - if ( compConfig ) { - for (const auto& itr : compConfig ) { - const auto name = itr.first.as<std::string>(); - const auto& config = itr.second; - - auto compType = m_types.find( name.c_str() ); - addFromConfig(entity, compType, config); - } - } - } - - inline auto alive(entity_t entity) const -> bool { - return entity != NULL_ENTITY - && m_killList.find( entity ) == m_killList.end() - && m_entities.find( entity ) != m_entities.end(); - } - - inline auto exists(entity_t entity) const -> bool { - return entity != NULL_ENTITY - && m_entities.find( entity ) != m_entities.end(); - } - - void destroy(entity_t entity) { - assert( alive(entity) && "attempted to kill null entity" ); - // TOOD resolve and clean components - //m_entities.erase(entity); - m_killList.insert(entity); - } - - void reapEntities() { - for (const auto& entity : m_killList) { - //auto& entityObj = m_entities.at(entity); - //entityObj.clear(); - for (auto& listener : m_deathListeners) { - listener( entity ); - } - - m_entities.erase(entity); - } - m_killList.clear(); - } - - inline TypeRegistry &types() { - return m_types; - } - - std::vector<entity_t> all() { - std::vector<entity_t> entities{}; - for (auto &[eid, entity] : m_entities) { - entities.push_back(eid); - } - return entities; - } - - std::vector<component_type_t> getComponents(entity_t entityID) { - assert(alive(entityID) && "attempted to get components on dead entity"); - - std::vector<component_type_t> components{}; - auto &entity = m_entities.at(entityID); - auto comps = entity.getComponentIDs(); - for (auto id : comps) { - components.push_back(id); - } - return components; - } - - template<typename... Cs> - std::vector<entity_t> findMatching() const { - // construct the key - std::vector<ecs::component_type_t> key; - (key.push_back(Component<Cs>::typeID()), ...); - - // entities - std::vector<entity_t> entities{}; - for (auto &[eid, entity] : m_entities) { - if (entity.hasComponents(key) && !entity.m_abstract) { - entities.push_back(eid); - } - } - - return entities; - } - - template<typename ...Cs> - bool has(entity_t entityIdx) const { - if ( !alive(entityIdx)) { - return false; - } - - std::vector<ecs::component_type_t> key; - (key.push_back(Component<Cs>::typeID()), ...); - return m_entities.at(entityIdx).hasComponents(key); - } - - template<typename C> - C *add(entity_t entity_id) { - assert( alive(entity_id) && "attempted to add component on null entity" ); - - //spdlog::info("component '{}' added to '{}'", C::name, entity_id); - auto &entity = m_entities.at(entity_id); - auto comp = entity.template add<C>(); - - m_types.fireAdd(this, entity_id, Component<C>::typeID()); - return comp; - } - - void *add(entity_t entity_id, component_type_t component_id) { - assert( alive(entity_id) && "attempted to add component on null entity" ); - - auto meta = m_types.meta(component_id); - auto &entity = m_entities.at(entity_id); - - void *ptr = entity.add(meta); - m_types.fireAdd(this, entity_id, meta->id()); - return ptr; - } - - template<typename C> - C *set(entity_t entity_id, const C *ptr) { - assert( alive( entity_id ) && "attempted to set component on null entity" ); - - //spdlog::info("component '{}' set on '{}'", C::name, entity_id); - auto &entity = m_entities.at(entity_id); - auto comp = entity.set<C>(ptr); - - m_types.fireAdd(this, entity_id, Component<C>::typeID()); - return comp; - } - - void *set(entity_t entity_id, component_type_t cid, const void *ptr) { - assert( alive( entity_id ) && "attempted to set component on null entity" ); - - auto &entity = m_entities.at(entity_id); - auto cMeta = m_types.meta(cid); - - auto comp = entity.set(cMeta, ptr); - m_types.fireAdd(this, entity_id, cid); - return comp; - } - - template<typename C> - C* tryGet(entity_t entity_id) const { - if ( entity_id == NULL_ENTITY) { - return nullptr; - } - - try { - return get<C>(entity_id); - } catch ( std::out_of_range& e) { - fggl::debug::info("component {} on {} did not exist", C::name, entity_id); - return nullptr; - } - } - - template<typename C> - C *get(entity_t entity_id) const { - assert( exists(entity_id) && "attempted to get component on null entity" ); - - const auto& entity = m_entities.at(entity_id); - return entity.get<C>(); - } - - template<typename C> - void remove(entity_t entity_id) { - assert( alive(entity_id) && "attempted to remove component on null entity" ); - - try { - auto &entity = m_entities.at(entity_id); - try { - return entity.remove<C>(); - } catch ( std::out_of_range& e ) { - std::cerr << "entity " << entity_id << " does not have component "<< C::name << std::endl; - } - } catch ( std::out_of_range& e) { - std::cerr << "tried to delete component on entity that didn't exist, entity was: " << entity_id << std::endl; - } - } - - void *get(entity_t entity_id, component_type_t componentType) { - assert( exists(entity_id) && "attempted to get component on null entity" ); - auto &entity = m_entities.at(entity_id); - return entity.get(componentType); - } - - void addDeathListener(const EntityCallback& callback) { - m_deathListeners.emplace_back(callback); - } - - entity_t findPrototype(const std::string& name) const { - for ( const auto& [entity, obj] : m_entities ) { - if ( !obj.m_abstract ){ - continue; - } - - auto* metaData = obj.get<EntityMeta>(); - if ( metaData->typeName == name){ - return entity; - } - } - - return NULL_ENTITY; - } - - private: - std::vector<EntityCallback > m_deathListeners; - TypeRegistry &m_types; - entity_t m_next; - std::map<entity_t, Entity> m_entities; - std::unordered_set<entity_t> m_killList; - - }; - -} - -#endif //FGGL_ECS3_PROTOTYPE_WORLD_HPP diff --git a/include/fggl/ecs3/types.hpp b/include/fggl/ecs3/types.hpp deleted file mode 100644 index 5947f599fb3bfbcad0409e00b43e08c86658ab7a..0000000000000000000000000000000000000000 --- a/include/fggl/ecs3/types.hpp +++ /dev/null @@ -1,242 +0,0 @@ -/* - * This file is part of FGGL. - * - * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with FGGL. - * If not, see <https://www.gnu.org/licenses/>. - */ - -#ifndef FGGL_ECS3_TYPES_HPP -#define FGGL_ECS3_TYPES_HPP - -#include <cstdarg> -#include <utility> -#include <functional> - -#include <fggl/ecs/component.hpp> -#include <fggl/ecs3/utils.hpp> - -#include <iostream> -#include <memory> -#include <algorithm> -#include <map> -#include <unordered_map> - -namespace fggl::ecs3 { - - namespace { - using namespace fggl::ecs; - }; - - namespace prototype { - class World; - } - - using fggl::ecs::component_type_t; - - class ModuleManager; - - using callback_t = std::function<void(prototype::World *, ecs3::entity_t)>; - struct TypeCallbacks { - std::vector<callback_t> add; - }; - - // core component types - struct EntityMeta { - constexpr static const char name[] = "meta"; - entity_t id; - bool abstract; - std::string typeName; - }; - - struct RecordIdentifier { - constexpr static std::size_t MAX_COMPS = 32; - component_type_t types[MAX_COMPS]; - std::size_t count; - - [[nodiscard]] - inline std::size_t idx(component_type_t t) const { - return utils::search(types, count, t); - } - - template<typename T> - [[nodiscard]] - RecordIdentifier with() const { - // check the caller wasn't a muppet - const auto typeID = ecs::Component<T>::typeID(); - if (idx(typeID) != count) { - return *this; - } - - RecordIdentifier re{}; - re.count = count + 1; - re.types[count] = ecs::Component<T>::typeID(); - - // add old types - for (std::size_t i = 0; i < count; ++i) { - re.types[i] = types[i]; - } - std::sort(re.types, re.types + re.count); - return re; - } - - template<typename T> - [[nodiscard]] - RecordIdentifier without() const { - // check the caller wasn't a muppet - const auto typeID = ecs::Component<T>::typeID(); - const auto typeIdx = idx(typeID); - if (typeIdx == count) { - return *this; - } - - RecordIdentifier re{}; - re.count = count - 1; - - // add old types - for (std::size_t i = 0, j = 0; i < count; ++i) { - if (typeIdx != i) { - re.types[j] = types[i]; - j++; - } - } - std::sort(re.types, re.types + re.count); - return re; - } - - bool operator<(const RecordIdentifier &other) const { - if (count < other.count) { - return true; - } else if (count > other.count) { - return false; - } else { - for (std::size_t i = 0; i < count; i++) { - if (types[i] != other.types[i]) { - return types[i] < other.types[i]; - } - } - return false; - } - } - - bool operator==(const RecordIdentifier &arg) const { - if (arg.count != count) { - return false; - } - - for (std::size_t i = 0; i < count; i++) { - if (types[i] != arg.types[i]) { - return false; - } - } - return true; - } - - bool operator!=(const RecordIdentifier &arg) const { - return !(*this == arg); - } - }; - - std::ostream &operator<<(std::ostream &out, RecordIdentifier const &curr); - - inline RecordIdentifier make_id(std::size_t count, ...) { - assert(count < RecordIdentifier::MAX_COMPS); - - RecordIdentifier re{}; - - std::va_list args; - va_start(args, count); - for (std::size_t i = 0; i < count; ++i) { - re.types[i] = va_arg(args, component_type_t); - } - va_end(args); - re.count = count; - std::sort(re.types, re.types + count); - - return re; - } - - class TypeRegistry { - public: - - TypeRegistry() : m_last_virtual(9000), m_callbacks() { - // core types always exist - make<EntityMeta>(); - } - - template<typename T> - void make() { - auto type_id = Component<T>::typeID(); - if (m_types.find(type_id) != m_types.end()) - return; - m_types[type_id] = std::make_shared<Component<T>>(); - } - - template<typename T> - bool exists() { - auto type_id = Component<T>::typeID(); - return m_types.find(type_id) != m_types.end(); - } - - template<typename T> - std::shared_ptr<fggl::ecs::ComponentBase> meta() const { - auto type_id = Component<T>::typeID(); - return m_types.at(type_id); - } - - inline std::shared_ptr<fggl::ecs::ComponentBase> meta(component_type_t type_id) const { - try { - return m_types.at(type_id); - } catch (std::out_of_range& err) { - std::cerr << "asked for metadata on type " << type_id << " but no such type is in the type system" << std::endl; - return nullptr; - } - } - - inline component_type_t find(const char *name) const { - for (const auto &[type, meta] : m_types) { - if (std::strcmp(name, meta->name()) == 0) { - return type; - } - } - - debug::warning("asked for unknown/unregistered component type: {}", name); - assert(false && "unknown component type, are you sure it was registered?"); - return 0; - } - - inline void make_virtual(std::shared_ptr<ComponentBase> vtype) { - auto type_id = m_last_virtual++; - m_types[type_id] = std::move(vtype); - } - - void callbackAdd(component_type_t component, const callback_t &callback) { - m_callbacks[component].add.push_back(callback); - } - - void fireAdd(prototype::World *world, entity_t entity, component_type_t type) { - try { - auto &callbacks = m_callbacks.at(type).add; - for (auto &callback : callbacks) { - callback(world, entity); - } - } catch (std::out_of_range &e) { - //spdlog::debug("no callbacks for {}", m_types[type]->name()); - } - } - - private: - std::unordered_map<component_type_t, std::shared_ptr<fggl::ecs::ComponentBase>> m_types; - component_type_t m_last_virtual; - std::map<component_type_t, TypeCallbacks> m_callbacks; - }; - -}; - -#endif diff --git a/include/fggl/entity/entity.hpp b/include/fggl/entity/entity.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5d12ccb985527ff8fa1e1aba29c77e942ec1c9e7 --- /dev/null +++ b/include/fggl/entity/entity.hpp @@ -0,0 +1,107 @@ +/* + * This file is part of FGGL. + * + * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with FGGL. + * If not, see <https://www.gnu.org/licenses/>. + */ + +// +// Created by webpigeon on 24/07/22. +// + +#ifndef FGGL_ENTITY_ENTITY_HPP +#define FGGL_ENTITY_ENTITY_HPP + +#include <cstdint> +#include "fggl/vendor/entt.hpp" + +namespace fggl::entity { + + using EntityID = entt::entity; + constexpr EntityID INVALID = entt::null; + + class EntityManager { + public: + inline EntityID create() { + return m_registry.create(); + } + inline void destroy(EntityID entity) { + m_registry.destroy(entity); + } + + template<typename Component, typename... Args> + inline Component& add(EntityID entity, Args&&... args) { + return m_registry.emplace<Component>(entity, std::forward<Args>(args)...); + } + + template<typename Component> + Component& get(EntityID entity) { + return m_registry.get<Component>(entity); + } + + template<typename Component> + const Component& get(EntityID entity) const { + return m_registry.get<Component>(entity); + } + template<typename Component> + Component* tryGet(EntityID entity) { + return m_registry.try_get<Component>(entity); + } + + template<typename Component> + const Component* tryGet(EntityID entity) const { + return m_registry.try_get<Component>(entity); + } + + template<typename ...Components> + auto find() const { + return m_registry.view<Components...>(); + } + + template<typename ...Components> + bool has(EntityID idx) const { + return m_registry.template all_of<Components...>(idx); + } + + inline bool exists(EntityID idx) { + return m_registry.valid(idx); + } + + inline bool alive(EntityID idx) { + return m_registry.valid(idx); + } + + + private: + entt::registry m_registry; + }; + + struct Entity { + static Entity make(EntityManager& manager, EntityID idx) { + return Entity{idx, manager}; + } + + EntityID id; + EntityManager& manager; + + template<typename Component> + Component& get() { + return manager.get<Component>(id); + } + + template<typename Component> + const Component& get() const { + return manager.get<Component>(id); + } + }; + +} // namespace fggl::entity + +#endif //FGGL_ENTITY_ENTITY_HPP diff --git a/include/fggl/entity/loader/loader.hpp b/include/fggl/entity/loader/loader.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8da4709a94c70f2a1e052e5059311db935b2174f --- /dev/null +++ b/include/fggl/entity/loader/loader.hpp @@ -0,0 +1,92 @@ +/* + * This file is part of FGGL. + * + * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with FGGL. + * If not, see <https://www.gnu.org/licenses/>. + */ + +// +// Created by webpigeon on 23/07/22. +// + +#ifndef FGGL_ENTITY_LOADER_LOADER_HPP +#define FGGL_ENTITY_LOADER_LOADER_HPP + +#include <functional> +#include <map> +#include <utility> + +#include "fggl/util/guid.hpp" +#include "fggl/modules/module.hpp" +#include "fggl/entity/entity.hpp" +#include "fggl/entity/loader/spec.hpp" +#include "fggl/assets/loader.hpp" + +namespace fggl::entity { + + constexpr auto PROTOTYPE_ASSET = assets::AssetType::make("entity_prototype"); + using FactoryFunc = std::function<void(const ComponentSpec& config, EntityManager&, const EntityID&)>; + + using ComponentID = util::GUID; + using EntityType = util::GUID; + + class EntityFactory { + public: + constexpr static const modules::ModuleService service = modules::make_service("fggl::entity:Factory"); + + EntityID create(const EntityType& spec, EntityManager& manager) { + try { + auto &prototype = m_prototypes.at(spec); + + // invoke each component factory as required + auto entity = manager.create(); + for (auto &[name, data] : prototype.components) { + try { + m_factories.at(name)(data, manager, entity); + } catch (std::out_of_range& ex) { + #ifndef NDEBUG + debug::log(debug::Level::error, "EntityFactory: Unknown component factory type '{}'", fggl::util::guidToString(name)); + #endif + manager.destroy(entity); + return fggl::entity::INVALID; + } + } + return entity; + } catch (std::out_of_range& ex) { + #ifndef NDEBUG + debug::log(debug::Level::error, "EntityFactory: Unknown entity type '{}'", fggl::util::guidToString(spec)); + #endif + return fggl::entity::INVALID; + } + } + + void define(EntityType type, const EntitySpec& spec) { + m_prototypes[type] = spec; + } + + // ability to set and unset factory functions + inline void bind(const ComponentID& configNode, FactoryFunc factory) { + m_factories[configNode] = std::move(factory); + } + + inline void unbind(const ComponentID& configNode) { + m_factories.erase(configNode); + } + + private: + std::map<ComponentID, FactoryFunc> m_factories; + std::map<EntityType, EntitySpec> m_prototypes; + }; + + assets::AssetRefRaw load_prototype(EntityFactory* factory, const assets::AssetGUID& guid, assets::AssetData data); + +} // namespace fggl::entity + +#endif //FGGL_ENTITY_LOADER_LOADER_HPP diff --git a/include/fggl/entity/loader/serialise.hpp b/include/fggl/entity/loader/serialise.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6650351fcef816b2a8478c9cce6babd2616740ff --- /dev/null +++ b/include/fggl/entity/loader/serialise.hpp @@ -0,0 +1,121 @@ +/* + * This file is part of FGGL. + * + * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with FGGL. + * If not, see <https://www.gnu.org/licenses/>. + */ + +// +// Created by webpigeon on 24/07/22. +// + +#ifndef FGGL_ENTITY_LOADER_SERIALISE_HPP +#define FGGL_ENTITY_LOADER_SERIALISE_HPP + +#include "yaml-cpp/yaml.h" + +#include "fggl/math/types.hpp" +#include "fggl/data/model.hpp" + +namespace YAML { + + template<> + struct convert<fggl::math::vec3> { + static Node encode(const fggl::math::vec3& rhs) { + Node node; + node.push_back(rhs.x); + node.push_back(rhs.y); + node.push_back(rhs.z); + return node; + } + + static bool decode(const Node& node, fggl::math::vec3& rhs) { + if (!node.IsSequence() || node.size() != 3) { + return false; + } + + rhs.x = node[0].as<float>(); + rhs.y = node[0].as<float>(); + rhs.z = node[0].as<float>(); + return true; + } + }; + + template<> + struct convert<fggl::math::vec2> { + static Node encode(const fggl::math::vec2& rhs) { + Node node; + node.push_back(rhs.x); + node.push_back(rhs.y); + return node; + } + + static bool decode(const Node& node, fggl::math::vec2& rhs) { + if (!node.IsSequence() || node.size() != 2) { + return false; + } + + rhs.x = node[0].as<float>(); + rhs.y = node[0].as<float>(); + return true; + } + }; + + template<> + struct convert<fggl::data::Vertex> { + static Node encode(const fggl::data::Vertex& rhs) { + Node node; + node["position"] = rhs.posititon; + node["normal"] = rhs.normal; + node["colour"] = rhs.colour; + node["texPos"] = rhs.texPos; + return node; + } + + static bool decode(const Node& node, fggl::data::Vertex& rhs) { + if (!node.IsSequence() || node.size() != 2) { + return false; + } + + rhs.posititon = node["position"].as<fggl::math::vec3>(); + rhs.normal = node["normal"].as<fggl::math::vec3>(); + rhs.colour = node["colour"].as<fggl::math::vec3>(); + rhs.texPos = node["texPos"].as<fggl::math::vec2>(); + return true; + } + }; + + template<> + struct convert<fggl::util::GUID> { + static Node encode(const fggl::util::GUID& rhs) { + Node node; + node = rhs.get(); + return node; + } + + static bool decode(const Node& node, fggl::util::GUID& rhs) { + auto longVal = node.as<uint64_t>(0); + + if ( longVal == 0 ) { + // probably meant to hash it... + auto stringVal = node.as<std::string>(); + rhs = fggl::util::make_guid_rt(stringVal); + return true; + } + + // it's probably pre-hashed... + rhs = fggl::util::GUID::make(longVal); + return true; + } + }; + +} + +#endif //FGGL_ENTITY_LOADER_SERIALISE_HPP diff --git a/include/fggl/entity/loader/spec.hpp b/include/fggl/entity/loader/spec.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4dd926c4dd73a3827788c75eb46fb9b9b90b24da --- /dev/null +++ b/include/fggl/entity/loader/spec.hpp @@ -0,0 +1,54 @@ +/* + * This file is part of FGGL. + * + * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with FGGL. + * If not, see <https://www.gnu.org/licenses/>. + */ + +// +// Created by webpigeon on 23/07/22. +// + +#ifndef FGGL_ENTITY_LOADER_SPEC_HPP +#define FGGL_ENTITY_LOADER_SPEC_HPP + +#include "fggl/util/guid.hpp" +#include "fggl/entity/loader/serialise.hpp" + +#include <map> + +namespace fggl::entity { + + struct ComponentSpec { + + template<typename T> + T get(const std::string& key, const T& fallback) const { + return config[key].template as<T>(fallback); + } + + template<typename T> + void set(const std::string& key, const T& value) { + config[key] = value; + } + + inline bool has(const std::string& key) const { + return (bool)(config[key]); + } + + YAML::Node config; + }; + + struct EntitySpec { + std::map<util::GUID, ComponentSpec> components; + }; + +} // namespace fggl::entity + +#endif //FGGL_ENTITY_LOADER_SPEC_HPP diff --git a/include/fggl/ecs/types.hpp b/include/fggl/entity/module.hpp similarity index 50% rename from include/fggl/ecs/types.hpp rename to include/fggl/entity/module.hpp index 92f727dd24f0864829f7e1bb2bded988daac3eb2..0b70faaf3b59c3725f7136404f0604cb382891a0 100644 --- a/include/fggl/ecs/types.hpp +++ b/include/fggl/entity/module.hpp @@ -13,27 +13,33 @@ */ // -// Created by webpigeon on 22/06/22. +// Created by webpigeon on 24/07/22. // -#ifndef FGGL_ECS_TYPES_HPP -#define FGGL_ECS_TYPES_HPP +#ifndef FGGL_ENTITY_MODULE_HPP +#define FGGL_ENTITY_MODULE_HPP -#include <cstdint> +#include "fggl/modules/module.hpp" +#include "fggl/assets/loader.hpp" -namespace fggl::ecs { +#include "fggl/entity/loader/loader.hpp" - struct entity { - const std::uint32_t id; - }; - constexpr const entity NULL_ENTITY = {0}; - constexpr const entity MAX_ENTITY = {0xFFFFFFFF}; +namespace fggl::entity { - struct componentID { - const std::uint32_t id; + struct ECS { + constexpr static const char* name = "fggl::entity::ECS"; + constexpr static const std::array<modules::ModuleService, 1> provides = { + EntityFactory::service + }; + constexpr static const std::array<modules::ModuleService, 1> depends = { + assets::Loader::service + }; + static const modules::ServiceFactory factory; }; - constexpr const entity NULL_COMPONENT = {0}; -} + void install_component_factories(EntityFactory* factory); + + +} // namespace fggl::entity -#endif //FGGL_ECS_TYPES_HPP +#endif //FGGL_ENTITY_MODULE_HPP diff --git a/include/fggl/gfx/camera.hpp b/include/fggl/gfx/camera.hpp index c1fcf989dcd5a2ff2c651cf5e97d75c26557fb9d..0968899ecbe1dce006040c713d9224f9d7290d50 100644 --- a/include/fggl/gfx/camera.hpp +++ b/include/fggl/gfx/camera.hpp @@ -15,7 +15,8 @@ #ifndef FGGL_GFX_CAMERA_HPP #define FGGL_GFX_CAMERA_HPP -#include <fggl/math/types.hpp> +#include "fggl/math/types.hpp" +#include "fggl/entity/entity.hpp" namespace fggl::gfx { @@ -28,13 +29,13 @@ namespace fggl::gfx { float farPlane = 100.0f; }; - inline math::mat4 calc_proj_matrix(const Camera* camera) { - return glm::perspective(camera->fov, camera->aspectRatio, camera->nearPlane, camera->farPlane); + inline math::mat4 calc_proj_matrix(const Camera& camera) { + return glm::perspective(camera.fov, camera.aspectRatio, camera.nearPlane, camera.farPlane); } - inline math::Ray get_camera_ray(const ecs3::World& world, const ecs3::entity_t camera, math::vec2 position) { - auto* const camTransform = world.get<fggl::math::Transform>(camera); - auto* const camComp = world.get<fggl::gfx::Camera>(camera); + inline math::Ray get_camera_ray(const entity::EntityManager& world, const entity::EntityID camera, math::vec2 position) { + auto& camTransform = world.get<fggl::math::Transform>(camera); + auto& camComp = world.get<fggl::gfx::Camera>(camera); const auto projMatrix = fggl::gfx::calc_proj_matrix(camComp); const auto viewMatrix = fggl::math::calc_view_matrix(camTransform); diff --git a/include/fggl/gfx/interfaces.hpp b/include/fggl/gfx/interfaces.hpp index f546ab2cd714d59c570d7941a5b080148531baae..2e22ed15adfdc23a4e9cb37bf48f5efb4f1a28a4 100644 --- a/include/fggl/gfx/interfaces.hpp +++ b/include/fggl/gfx/interfaces.hpp @@ -20,7 +20,7 @@ #define FGGL_GFX_INTERFACES_HPP #include "fggl/gfx/paint.hpp" -#include "fggl/ecs3/ecs.hpp" +#include "fggl/entity/entity.hpp" #include "fggl/modules/module.hpp" namespace fggl::gfx { @@ -43,7 +43,7 @@ namespace fggl::gfx { virtual Bounds canvasBounds() = 0; virtual void draw2D(const Paint &paint) = 0; - virtual void drawScene(ecs3::World&) = 0; + virtual void drawScene(entity::EntityManager&) = 0; }; diff --git a/include/fggl/gfx/ogl/compat.hpp b/include/fggl/gfx/ogl/compat.hpp index 3b9e3a280eebdb45a336bae7440668aeceda299a..71594771b19f7390b2ff6fe4d727f7d20850717e 100644 --- a/include/fggl/gfx/ogl/compat.hpp +++ b/include/fggl/gfx/ogl/compat.hpp @@ -31,7 +31,7 @@ #include <fggl/gfx/common.hpp> #include <fggl/gfx/camera.hpp> -#include <fggl/ecs/ecs.hpp> + #include <utility> #include <fggl/input/camera_input.hpp> #include <fggl/data/heightmap.hpp> @@ -43,7 +43,7 @@ namespace fggl::gfx { // // fake module support - allows us to still RAII // - struct SceneUtils : ecs3::Module { + /*struct SceneUtils : ecs3::Module { SceneUtils() = default; @@ -65,7 +65,7 @@ namespace fggl::gfx { types.make<fggl::input::FreeCamKeys>(); } - }; + };*/ } diff --git a/include/fggl/gfx/ogl/renderer.hpp b/include/fggl/gfx/ogl/renderer.hpp index acda7312f443debd7e776882ac841dc1b8ff7951..4b5311e30281d651c41adb261f240b2d544f3af8 100644 --- a/include/fggl/gfx/ogl/renderer.hpp +++ b/include/fggl/gfx/ogl/renderer.hpp @@ -16,10 +16,9 @@ #define FGGL_GFX_OGL_RENDERER_HPP #include <fggl/data/model.hpp> -#include <fggl/ecs3/ecs.hpp> + #include <fggl/gfx/ogl/backend.hpp> #include <fggl/gfx/ogl/shader.hpp> - #include "fggl/gfx/ogl4/models.hpp" #include "fggl/gfx/ogl4/canvas.hpp" #include "fggl/gfx/ogl4/debug.hpp" @@ -47,17 +46,6 @@ namespace fggl::gfx { GlRenderType renderType = triangles; }; - class GlMeshRenderer { - public: - using token_t = GlRenderToken; - - token_t upload(fggl::data::Mesh &mesh); - - void render(ecs3::World &ecs, ecs3::entity_t camera, float dt); - - float total; - }; - /** * Class responsible for managing the OpenGL context. * @@ -103,7 +91,7 @@ namespace fggl::gfx { * * @param world the world to render */ - void drawScene(ecs3::World& world) override; + void drawScene(entity::EntityManager& world) override; /** * Get the 2D canvas bounds. diff --git a/include/fggl/gfx/ogl/types.hpp b/include/fggl/gfx/ogl/types.hpp index 73b1b447c5cee86ce616d285108de40df61e4006..aefb536be486e0f239ae69f8e8d072405720c1b8 100644 --- a/include/fggl/gfx/ogl/types.hpp +++ b/include/fggl/gfx/ogl/types.hpp @@ -263,7 +263,7 @@ namespace fggl::gfx::ogl { * @param textureUnit the texture unit to bind to */ inline void bind(unsigned int textureUnit) { - assert( 0 <= textureUnit && textureUnit < GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS ); + assert( textureUnit < GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS ); glActiveTexture( GL_TEXTURE0 + textureUnit ); glBindTexture( (GLenum) m_type, m_obj ); } diff --git a/include/fggl/gfx/ogl4/models.hpp b/include/fggl/gfx/ogl4/models.hpp index fab456ebf757e291bec8ab9154cd708b7a54c353..1a722430ce5d49f5c6436cd1090950d61589b363 100644 --- a/include/fggl/gfx/ogl4/models.hpp +++ b/include/fggl/gfx/ogl4/models.hpp @@ -25,7 +25,6 @@ #include "fggl/gfx/ogl/shader.hpp" #include "fggl/gfx/ogl/backend.hpp" #include "fggl/gfx/ogl/types.hpp" -#include "fggl/ecs3/ecs.hpp" namespace fggl::gfx::ogl4 { @@ -59,7 +58,7 @@ namespace fggl::gfx::ogl4 { StaticModelRenderer& operator=(const StaticModelRenderer& other) = delete; StaticModelRenderer& operator=(StaticModelRenderer&& other) = delete; - void render(ecs3::World& world) { + void render(entity::EntityManager& world) { resolveModels(world); renderModelsForward(world); } @@ -68,12 +67,12 @@ namespace fggl::gfx::ogl4 { /** * Attach any missing rendering components to models. */ - void resolveModels(ecs3::World& world); + void resolveModels(entity::EntityManager& world); /** * Render all visible objects according to their render tokens. */ - void renderModelsForward(const ecs3::World& world); + void renderModelsForward(const entity::EntityManager& world); gfx::ShaderCache* m_shaders; std::shared_ptr< ogl::Shader > m_phong; diff --git a/include/fggl/gfx/ogl4/module.hpp b/include/fggl/gfx/ogl4/module.hpp index 6dc9be3226fdbe14bacb6a383513e2256c8026f7..c9501b5fc6f705924dedcde027733fb16bfa2631 100644 --- a/include/fggl/gfx/ogl4/module.hpp +++ b/include/fggl/gfx/ogl4/module.hpp @@ -21,6 +21,8 @@ #include <array> #include "fggl/modules/module.hpp" +#include "fggl/entity/loader/loader.hpp" + #include "fggl/gfx/interfaces.hpp" #include "fggl/gfx/setup.hpp" @@ -34,24 +36,14 @@ namespace fggl::gfx { constexpr static const std::array<modules::ModuleService, 1> provides = { WindowGraphics::service }; - constexpr static const std::array<modules::ModuleService, 2> depends = { + constexpr static const std::array<modules::ModuleService, 3> depends = { data::Storage::service, - gui::FontLibrary::service + gui::FontLibrary::service, + entity::EntityFactory::service }; static const modules::ServiceFactory factory; }; - bool ogl4_factory(modules::ModuleService service, modules::Services& services) { - if (service == WindowGraphics::service) { - auto storage = services.get<data::Storage>(); - auto font_library = services.get<gui::FontLibrary>(); - services.bind<WindowGraphics, ogl4::WindowGraphics>(storage, font_library); - return true; - } - return false; - } - const modules::ServiceFactory OpenGL4::factory = ogl4_factory; - } //namespace fggl::gfx #endif //FGGL_GFX_OGL4_MODULE_HPP diff --git a/include/fggl/gfx/ogl4/setup.hpp b/include/fggl/gfx/ogl4/setup.hpp index 2da109c19afb012ecaca35dc742f471e16a0d396..9c0f64d766383dac31da418ffdad1c57fc10a113 100644 --- a/include/fggl/gfx/ogl4/setup.hpp +++ b/include/fggl/gfx/ogl4/setup.hpp @@ -47,9 +47,6 @@ namespace fggl::gfx::ogl4 { gui::FontLibrary* m_fonts; }; - fggl::gfx::Graphics *WindowGraphics::create(display::Window &window) { - return new OpenGL4Backend(m_storage, m_fonts); - } } // namespace fggl::gfx::ogl4 diff --git a/include/fggl/gfx/phong.hpp b/include/fggl/gfx/phong.hpp index e62773e9ae0b0be94194f14a305be95a34841bdb..0dc5f822d9f32cfabe5c4d54344e7194758a403e 100644 --- a/include/fggl/gfx/phong.hpp +++ b/include/fggl/gfx/phong.hpp @@ -20,11 +20,13 @@ #define FGGL_GFX_PHONG_HPP #include "fggl/math/types.hpp" +#include "fggl/util/guid.hpp" namespace fggl::gfx { struct PhongMaterial { constexpr static const char* name = "gfx::material"; + constexpr static const util::GUID guid = util::make_guid("gfx::material"); math::vec3 emission; math::vec3 ambient; math::vec3 diffuse; @@ -49,6 +51,7 @@ namespace fggl::gfx { struct Light { constexpr static const char* name = "gfx::light"; + constexpr static const util::GUID guid = util::make_guid("gfx::light"); bool enabled; bool local; bool spot; diff --git a/include/fggl/gui/widget.hpp b/include/fggl/gui/widget.hpp index f40967bab2e0863138164e1729e4b6f0532241bf..217b31b072d683ae0f48dfb3d725a9c41c9f20ac 100644 --- a/include/fggl/gui/widget.hpp +++ b/include/fggl/gui/widget.hpp @@ -22,8 +22,17 @@ namespace fggl::gui { - constexpr float RGB_MAX_VAL = 255.0F; - constexpr math::vec3 rgbToNormal(char red, char green, char blue) { + constexpr uint8_t RGB_MAX_VAL = 255; + + /** + * Convert an RGB value using 0-255 to a 0-1 value. + * + * @param red red component + * @param green green component + * @param blue blue component + * @return the normalized RGB value + */ + constexpr math::vec3 rgbToNormal(uint8_t red, uint8_t green, uint8_t blue) { return {red / RGB_MAX_VAL, green / RGB_MAX_VAL, blue / RGB_MAX_VAL}; } @@ -41,7 +50,7 @@ namespace fggl::gui { assert( 0 < value && value <= 1); const float chroma = value * saturation; - const float x = chroma * (1 - fabs( fmod(hue / 60.0F, 2) - 1) ); + const float x = chroma * (1 - std::fabs( std::fmod(hue / 60.0F, 2.0F) - 1.0F) ); math::vec3 tmp{0,0,0}; if ( 0 <= hue && hue < 60 ) { tmp = {chroma,x,0}; diff --git a/include/fggl/input/camera_input.hpp b/include/fggl/input/camera_input.hpp index db85605464e4d9021c2cbe19eb373f546a940e2f..6b549761e26a1f7abd897e79ed08c013e44a63cf 100644 --- a/include/fggl/input/camera_input.hpp +++ b/include/fggl/input/camera_input.hpp @@ -19,8 +19,8 @@ #ifndef FGGL_INPUT_CAMERA_INPUT_HPP #define FGGL_INPUT_CAMERA_INPUT_HPP -#include <fggl/ecs3/ecs.hpp> -#include <fggl/math/types.hpp> +#include "fggl/entity/entity.hpp" +#include "fggl/math/types.hpp" namespace fggl::input { @@ -38,7 +38,7 @@ namespace fggl::input { scancode_t rotate_ccw; }; - void process_scroll(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam, float minZoom = 10.0F, float maxZoom = 50.0F); + void process_scroll(entity::EntityManager &ecs, const Input &input, entity::EntityID cam, float minZoom = 10.0F, float maxZoom = 50.0F); /** * Process the camera based on rotation around a fixed point. @@ -47,7 +47,7 @@ namespace fggl::input { * @param input the input module to read the mouse location from * @param cam the ID of the camera entity */ - void process_arcball(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam); + void process_arcball(entity::EntityManager &ecs, const Input &input, entity::EntityID cam); /** * Process free (floating) camera movement. @@ -56,7 +56,7 @@ namespace fggl::input { * @param input the input module to read the mouse location from * @param cam the ID of the camera entity */ - void process_freecam(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam); + void process_freecam(entity::EntityManager &ecs, const Input &input, entity::EntityID cam); /** * Input processing for moving the camera when the mouse is close to the edge of the screen. @@ -69,7 +69,7 @@ namespace fggl::input { * @param input the input module to read the mouse location from * @param cam the ID of the camera entity */ - void process_edgescroll(fggl::ecs3::World &ecs, const Input &input, fggl::ecs::entity_t cam); + void process_edgescroll(entity::EntityManager &ecs, const Input &input, entity::EntityID cam); } diff --git a/include/fggl/input/gamepad.hpp b/include/fggl/input/gamepad.hpp index 0242d7f77512aea486346b6e0f8e6b2b2e48f1aa..fc583361512301b66d9d1b4cf59fd20aa65243ba 100644 --- a/include/fggl/input/gamepad.hpp +++ b/include/fggl/input/gamepad.hpp @@ -192,7 +192,7 @@ namespace fggl::input { return m_current[id].buttons[(int) btn] != m_previous[id].buttons[(int) btn]; } - inline void frame(float dt) { + inline void frame(float /*dt*/) { m_previous = m_current; } diff --git a/include/fggl/input/keyboard.hpp b/include/fggl/input/keyboard.hpp index 735cf66626f684f85bd4851199657a1ad9b1fcd7..aba2cee8ba29b17aa36579277c163004713046d0 100644 --- a/include/fggl/input/keyboard.hpp +++ b/include/fggl/input/keyboard.hpp @@ -52,7 +52,7 @@ namespace fggl::input { class KeyboardInput { public: - inline void frame(float dt) { + inline void frame(float /*dt*/) { m_prev = m_curr; } diff --git a/include/fggl/input/mouse.hpp b/include/fggl/input/mouse.hpp index 327d95c7b7111b6f858c8e2a41c765379a191713..f2900e1c1e8b240a91f1a6a477d8fa1b502387b9 100644 --- a/include/fggl/input/mouse.hpp +++ b/include/fggl/input/mouse.hpp @@ -80,7 +80,7 @@ namespace fggl::input { MouseInput(const MouseInput &rhs) = delete; void operator=(const MouseInput &rhs) = delete; - inline void frame(float dt) { + inline void frame(float /*dt*/) { m_prev = m_curr; } diff --git a/include/fggl/math/types.hpp b/include/fggl/math/types.hpp index ff88aaed52d8318d43a63d398c1b3e9a31816535..14cc4782f477c618751e12607eba05ca434dac83 100644 --- a/include/fggl/math/types.hpp +++ b/include/fggl/math/types.hpp @@ -25,6 +25,8 @@ #include <glm/gtx/euler_angles.hpp> #include <glm/gtx/quaternion.hpp> +#include "fggl/util/guid.hpp" + #ifndef M_PI #define M_PI 3.14159265358979323846 #endif @@ -119,7 +121,7 @@ namespace fggl::math { }; struct Transform { - constexpr static const char name[] = "Transform"; + constexpr static const util::GUID guid = util::make_guid("Transform"); Transform() : m_model(IDENTITY_M4), @@ -223,12 +225,12 @@ namespace fggl::math { }; - inline math::mat4 calc_view_matrix(const Transform* transform) { - return glm::lookAt(transform->origin(), transform->origin() + transform->forward(), transform->up()); + inline math::mat4 calc_view_matrix(const Transform& transform) { + return glm::lookAt(transform.origin(), transform.origin() + transform.forward(), transform.up()); } - inline math::mat4 calc_view_matrix(const Transform* transform, vec3 target) { - return glm::lookAt(transform->origin(), target, transform->up()); + inline math::mat4 calc_view_matrix(const Transform& transform, vec3 target) { + return glm::lookAt(transform.origin(), target, transform.up()); } } diff --git a/include/fggl/modules/manager.hpp b/include/fggl/modules/manager.hpp index 1348fdd162496fded8fb269e9fce7c8cab56bf67..d804da6f368400025c2b8a2a1c13eaac1d94eb1d 100644 --- a/include/fggl/modules/manager.hpp +++ b/include/fggl/modules/manager.hpp @@ -120,7 +120,7 @@ namespace fggl::modules { void use() { assert( !m_locked ); - Config config { .name = T::name }; + Config config { .name = T::name, .provides = {}, .depends = {} }; for ( auto service : T::provides ) { config.provides.push_back(service); } diff --git a/include/fggl/modules/module.hpp b/include/fggl/modules/module.hpp index 176b3de87687a2945e1bb9b345dc1e0f2204d5f8..5ff03174b1f71d0250c08917eee17e5b71890525 100644 --- a/include/fggl/modules/module.hpp +++ b/include/fggl/modules/module.hpp @@ -45,8 +45,10 @@ namespace fggl::modules { } template<typename Svc, typename ...Args> - void create(Args... args){ - m_services[Svc::service] = std::make_shared<Svc>(args...); + Svc* create(Args... args){ + auto svc = std::make_shared<Svc>(args...); + m_services[Svc::service] = svc; + return svc.get(); } template<typename Svc> diff --git a/include/fggl/phys/callbacks.hpp b/include/fggl/phys/callbacks.hpp index 9ff0eaf1b90dcf45099d212a066eb198cbdf4769..856be21bd24e42943694cf0ea69140f24a5a82cb 100644 --- a/include/fggl/phys/callbacks.hpp +++ b/include/fggl/phys/callbacks.hpp @@ -21,11 +21,11 @@ #include <functional> #include <unordered_set> -#include "fggl/ecs3/types.hpp" +#include "fggl/entity/entity.hpp" namespace fggl::phys { - using CollisionCB = std::function<void(ecs3::entity_t, ecs3::entity_t)>; + using CollisionCB = std::function<void(entity::EntityID , entity::EntityID)>; struct CollisionCallbacks { constexpr static const char* name = "phys::Callbacks"; @@ -36,8 +36,8 @@ namespace fggl::phys { struct CollisionCache { constexpr static const char* name = "phys::Cache"; - std::unordered_set<ecs3::entity_t> collisions; - std::unordered_set<ecs3::entity_t> lastFrame; + std::unordered_set<entity::EntityID> collisions; + std::unordered_set<entity::EntityID> lastFrame; }; } // namespace fggl::phys diff --git a/include/fggl/phys/types.hpp b/include/fggl/phys/types.hpp index 8d6bdee5989c67aa08eb0983c7646457e1612e10..e69c45aa5c3497a08ef163c50b4b782e9508e1d2 100644 --- a/include/fggl/phys/types.hpp +++ b/include/fggl/phys/types.hpp @@ -72,8 +72,8 @@ namespace fggl::phys { }; struct ContactPoint { - ecs3::entity_t entityA; - ecs3::entity_t entityB; + entity::EntityID entityA; + entity::EntityID entityB; math::vec3 localA; math::vec3 localB; math::vec3 normal; @@ -104,16 +104,15 @@ namespace fggl::phys { PhysicsEngine& operator=(PhysicsEngine&&) = delete; // query methods (first cut - unstable APIs) - virtual std::vector<ContactPoint> scanCollisions(ecs3::entity_t entity) = 0; - virtual ecs3::entity_t raycast(math::vec3 from, math::vec3 to) = 0; + virtual std::vector<ContactPoint> scanCollisions(entity::EntityID entity) = 0; - inline ecs3::entity_t raycast(math::Ray ray, float maxDist = 1000.0F) { + virtual entity::EntityID raycast(math::vec3 from, math::vec3 to) = 0; + inline entity::EntityID raycast(math::Ray ray, float maxDist = 1000.0F) { return raycast(ray.origin, ray.origin + ray.direction * maxDist); } - - virtual std::vector<ecs3::entity_t> raycastAll(math::vec3 from, math::vec3 to) = 0; - virtual std::vector<ecs3::entity_t> sweep(PhyShape& shape, math::Transform& from, math::Transform& to) = 0; + virtual std::vector<entity::EntityID> raycastAll(math::vec3 from, math::vec3 to) = 0; + virtual std::vector<entity::EntityID> sweep(PhyShape& shape, math::Transform& from, math::Transform& to) = 0; // update virtual void step() = 0; diff --git a/include/fggl/scenes/game.hpp b/include/fggl/scenes/game.hpp index 59970604fc2a67dd6245462cac6b1a349403b069..2b1ad8145e206f7c7c2c2abfe5c7961ec41fde91 100644 --- a/include/fggl/scenes/game.hpp +++ b/include/fggl/scenes/game.hpp @@ -20,6 +20,7 @@ #define FGGL_SCENES_GAME_HPP #include "fggl/app.hpp" +#include "fggl/entity/entity.hpp" #include "fggl/phys/types.hpp" namespace fggl::scenes { @@ -36,7 +37,7 @@ namespace fggl::scenes { void render(fggl::gfx::Graphics& gfx) override; protected: - inline auto world() -> ecs3::World& { + inline auto world() -> entity::EntityManager& { return *m_world; } @@ -50,7 +51,7 @@ namespace fggl::scenes { private: input::Input* m_input; - std::unique_ptr<ecs3::World> m_world; + std::unique_ptr<entity::EntityManager> m_world; std::unique_ptr<phys::PhysicsEngine> m_phys; std::string m_previous = "menu"; }; diff --git a/include/fggl/util/guid.hpp b/include/fggl/util/guid.hpp index c9c85b2db090d69700223911214b6ad3b982c683..5feca83726357effedc4d2417fe2cd7847c186a6 100644 --- a/include/fggl/util/guid.hpp +++ b/include/fggl/util/guid.hpp @@ -21,6 +21,8 @@ #define FGGL_UTIL_GUID_HPP #include <cstdint> +#include <cassert> + #include "fggl/util/safety.hpp" namespace fggl::util { @@ -64,13 +66,23 @@ namespace fggl::util { return hash; } + // debug-only functions + #ifndef NDEBUG + GUID internString(const char* str); + std::string guidToString(GUID guid); + #endif + constexpr GUID make_guid(const char* str) { return GUID::make(hash_fnv1a_64(str)); } - // debug-only functions - GUID internString(const char* str); - std::string guidToString(GUID guid); + inline GUID make_guid_rt(const std::string& str) { + #ifndef NDEBUG + return internString(str.c_str()); + #else + return make_guid(str.c_str()); + #endif + } } // namespace fggl::util diff --git a/include/fggl/util/safety.hpp b/include/fggl/util/safety.hpp index 9b9c0d281644130ce83d7f8a8e30d20e3d056ffc..e11a5723555cc17669fa8b1c354d7032dee0c35e 100644 --- a/include/fggl/util/safety.hpp +++ b/include/fggl/util/safety.hpp @@ -26,7 +26,7 @@ namespace fggl::util { /** * A type-safe opaque handle. * - * Lots of low-level libaries we use pass around handles as some primative type. It's fairly easy to accidentally + * Lots of low-level libraries we use pass around handles as some primative type. It's fairly easy to accidentally * mix these up. This wrapper's job is to make sure that mixing up handle types is impossible (and results in * compiler errors). * @@ -39,7 +39,12 @@ namespace fggl::util { T m_value; public: - constexpr OpaqueName(T value) : m_value(value) {} + explicit constexpr OpaqueName(T value) : m_value(value) {} + constexpr OpaqueName() : m_value() {} + + constexpr T get() const { + return m_value; + } constexpr explicit operator std::string_view() const { return m_value; } @@ -47,12 +52,20 @@ namespace fggl::util { return m_value == other.m_value; } + bool operator!=(const OpaqueName<T, Tag> &other) const { + return !(*this == other); + } + + bool operator<(const OpaqueName<T, Tag> &other) const { + return m_value < other.m_value; + } + std::strong_ordering operator<=>(const OpaqueName<T, Tag> &other) const noexcept { return m_value <=> other.m_value; } constexpr static OpaqueName<T, Tag> make(T value) { - return OpaqueName(value); + return OpaqueName<T, Tag>(value); } }; diff --git a/include/fggl/util/states.hpp b/include/fggl/util/states.hpp index fa2c713047e4b38e53ff43107691bceb042a8846..e5b81ccf2f2778f7c2788e9b13b4c917c48c48c7 100644 --- a/include/fggl/util/states.hpp +++ b/include/fggl/util/states.hpp @@ -20,6 +20,8 @@ #include <memory> #include <unordered_map> +#include "fggl/debug/logging.hpp" + namespace fggl::util { template<typename S, typename I> diff --git a/integrations/bullet/include/fggl/phys/bullet/bullet.hpp b/integrations/bullet/include/fggl/phys/bullet/bullet.hpp index fb28ce6110466b8ad56781d6289649d91cdf5777..a65250a19b480c6083880e66d727aa369655baac 100644 --- a/integrations/bullet/include/fggl/phys/bullet/bullet.hpp +++ b/integrations/bullet/include/fggl/phys/bullet/bullet.hpp @@ -35,8 +35,6 @@ #define FGGL_MODULE_BULLET fggl::phys::Bullet3 -#include "fggl/ecs3/module/module.hpp" - #include "fggl/phys/types.hpp" #include "fggl/phys/bullet/types.hpp" @@ -49,7 +47,7 @@ namespace fggl::phys::bullet { * engine which makes it suitable for most use cases. For use with FGGL there is a reasonable amount of copying * back and forth to keep the two states in sync. */ - struct BulletModule : ecs3::Module { + /*struct BulletModule : ecs3::Module { public: BulletModule() = default; @@ -58,7 +56,7 @@ namespace fggl::phys::bullet { return "phys::Bullet3"; } - void onLoad(ecs3::ModuleManager & /*manager*/, ecs3::TypeRegistry &types) override { + void onLoad(ecs3::ModuleManager&, ecs3::TypeRegistry &types) override { // dependencies types.make<phys::CollisionCallbacks>(); types.make<phys::CollisionCache>(); @@ -70,14 +68,14 @@ namespace fggl::phys::bullet { types.make<phys::bullet::BulletBody>(); } - }; + };*/ } // namespace fggl::phys::bullet namespace fggl::phys { // allows using fggl::phys::Bullet3 as the module name - using Bullet3 = bullet::BulletModule; + //using Bullet3 = bullet::BulletModule; } // namespace fggl::phys diff --git a/integrations/bullet/include/fggl/phys/bullet/motion.hpp b/integrations/bullet/include/fggl/phys/bullet/motion.hpp index 5b0540a62bb29fe96827c8771855a71bc638c9a6..7a38a2209b45a573df623ed20a3ccd2c7e67d38e 100644 --- a/integrations/bullet/include/fggl/phys/bullet/motion.hpp +++ b/integrations/bullet/include/fggl/phys/bullet/motion.hpp @@ -39,31 +39,31 @@ namespace fggl::phys::bullet { class FgglMotionState : public btMotionState { public: - FgglMotionState(fggl::ecs3::World* world, fggl::ecs3::entity_t entity) : m_world(world), m_entity(entity) { + FgglMotionState(entity::EntityManager* world, entity::EntityID entity) : m_world(world), m_entity(entity) { } virtual ~FgglMotionState() = default; void getWorldTransform(btTransform& worldTrans) const override { - const auto* transform = m_world->get<fggl::math::Transform>(m_entity); - worldTrans.setFromOpenGLMatrix( glm::value_ptr(transform->model()) ); + const auto& transform = m_world->get<fggl::math::Transform>(m_entity); + worldTrans.setFromOpenGLMatrix( glm::value_ptr(transform.model()) ); } void setWorldTransform(const btTransform& worldTrans) override { - auto* transform = m_world->get<fggl::math::Transform>(m_entity); + auto& transform = m_world->get<fggl::math::Transform>(m_entity); // set position auto btOrigin = worldTrans.getOrigin(); - transform->origin( {btOrigin.x(), btOrigin.y(), btOrigin.z()} ); + transform.origin( {btOrigin.x(), btOrigin.y(), btOrigin.z()} ); // set rotation math::vec3 angles; worldTrans.getRotation().getEulerZYX(angles.x, angles.y, angles.z); - transform->euler(angles); + transform.euler(angles); } private: - fggl::ecs3::World* m_world; - fggl::ecs3::entity_t m_entity; + entity::EntityManager* m_world; + entity::EntityID m_entity; }; } // namespace fggl::phys::bullet diff --git a/integrations/bullet/include/fggl/phys/bullet/types.hpp b/integrations/bullet/include/fggl/phys/bullet/types.hpp index 0809053ccd78d85f7bedf965bac26e8307aaef2d..4d6857d3c98c693992187a14d126c2ddcf1983b1 100644 --- a/integrations/bullet/include/fggl/phys/bullet/types.hpp +++ b/integrations/bullet/include/fggl/phys/bullet/types.hpp @@ -33,7 +33,6 @@ #ifndef FGGL_PHYS_BULLET_TYPES_HPP #define FGGL_PHYS_BULLET_TYPES_HPP -#include "fggl/ecs3/ecs.hpp" #include "fggl/phys/types.hpp" #include "phys_draw.hpp" @@ -73,20 +72,20 @@ namespace fggl::phys::bullet { */ class BulletPhysicsEngine : public PhysicsEngine { public: - explicit BulletPhysicsEngine(ecs3::World* world); + explicit BulletPhysicsEngine(entity::EntityManager* world); ~BulletPhysicsEngine() override; void step() override; - void onEntityDeath(ecs::entity_t entity); + void onEntityDeath(entity::EntityID entity); - std::vector<ContactPoint> scanCollisions(ecs3::entity_t entity) override; - ecs3::entity_t raycast(math::vec3 from, math::vec3 to) override; - std::vector<ecs3::entity_t> raycastAll(math::vec3 from, math::vec3 to) override; - std::vector<ecs3::entity_t> sweep(PhyShape& shape, math::Transform& from, math::Transform& to) override; + std::vector<ContactPoint> scanCollisions(entity::EntityID entity) override; + entity::EntityID raycast(math::vec3 from, math::vec3 to) override; + std::vector<entity::EntityID> raycastAll(math::vec3 from, math::vec3 to) override; + std::vector<entity::EntityID> sweep(PhyShape& shape, math::Transform& from, math::Transform& to) override; bool debugDraw = false; private: - ecs3::World* m_ecs; + entity::EntityManager* m_ecs; BulletConfiguration m_config; btDiscreteDynamicsWorld* m_world; std::unique_ptr<debug::BulletDebugDrawList> m_debug; diff --git a/integrations/bullet/src/simulation.cpp b/integrations/bullet/src/simulation.cpp index e4fbcbef75833e21509fcaa12647709ea902f33f..4ee06d6d197a63e595041cc5e6e177db6d065886 100644 --- a/integrations/bullet/src/simulation.cpp +++ b/integrations/bullet/src/simulation.cpp @@ -35,7 +35,7 @@ namespace fggl::phys::bullet { return {fgglVec.x, fgglVec.y, fgglVec.z}; } - BulletPhysicsEngine::BulletPhysicsEngine(ecs3::World* world) : m_ecs(world), m_config(), m_world(nullptr) { + BulletPhysicsEngine::BulletPhysicsEngine(entity::EntityManager* world) : m_ecs(world), m_config(), m_world(nullptr) { m_config.broadphase = new btDbvtBroadphase(); m_config.collisionConfiguration = new btDefaultCollisionConfiguration(); m_config.dispatcher = new btCollisionDispatcher(m_config.collisionConfiguration); @@ -54,30 +54,26 @@ namespace fggl::phys::bullet { // callbacks (for handling bullet -> ecs) // ensure we deal with ecs -> bullet changes - m_ecs->addDeathListener( [this](auto entity) { this->onEntityDeath(entity);} ); + //m_ecs->addDeathListener( [this](auto entity) { this->onEntityDeath(entity);} ); } void BulletPhysicsEngine::step() { checkForPhys(); - auto dynamicEnts = m_ecs->findMatching<phys::Dynamics>(); + auto dynamicEnts = m_ecs->find<phys::Dynamics, phys::bullet::BulletBody>(); for (auto& ent : dynamicEnts) { - auto* dynamicComp = m_ecs->get<phys::Dynamics>(ent); - auto* bulletProxy = m_ecs->get<phys::bullet::BulletBody>(ent); - if ( bulletProxy == nullptr ) { - std::cerr << "entity has dynamics but isn't a physics object!" << std::endl; - continue; - } + auto& dynamicComp = m_ecs->get<phys::Dynamics>(ent); + auto& bulletProxy = m_ecs->get<phys::bullet::BulletBody>(ent); - const auto forceVec = dynamicComp->force; + const auto forceVec = dynamicComp.force; if ( glm::length(forceVec) != 0.F ) { - bulletProxy->body->applyCentralForce({forceVec.x, forceVec.y, forceVec.z}); - bulletProxy->body->activate(); - dynamicComp->force = math::VEC3_ZERO; + bulletProxy.body->applyCentralForce({forceVec.x, forceVec.y, forceVec.z}); + bulletProxy.body->activate(); + dynamicComp.force = math::VEC3_ZERO; } } - m_world->stepSimulation(60.0f); + m_world->stepSimulation(60.0F); //syncToECS(); pollCollisions(); @@ -86,19 +82,19 @@ namespace fggl::phys::bullet { } } - inline btCollisionShape* shape_to_bullet(const phys::RigidBody* fgglBody) { - if ( fgglBody->shape == nullptr ) { + inline btCollisionShape* shape_to_bullet(const phys::RigidBody& fgglBody) { + if ( fgglBody.shape == nullptr ) { // they forgot to put a shape, we'll assume a unit sphere to avoid crashes... return new btSphereShape(0.5f); } - switch (fgglBody->shape->type) { + switch (fgglBody.shape->type) { case phys::ShapeType::BOX: { - auto extents = ((phys::Box*)fgglBody->shape)->extents; + auto extents = ((phys::Box*)fgglBody.shape)->extents; return new btBoxShape({extents.x, extents.y, extents.z}); } case phys::ShapeType::SPHERE: { - float radius = ((phys::Sphere*)fgglBody->shape)->radius; + float radius = ((phys::Sphere*)fgglBody.shape)->radius; return new btSphereShape(radius); } default: @@ -108,7 +104,7 @@ namespace fggl::phys::bullet { void BulletPhysicsEngine::checkForPhys() { // FIXME without reactive-based approaches this is very slow - auto entsWantPhys = m_ecs->findMatching<phys::RigidBody>(); + auto entsWantPhys = m_ecs->find<phys::RigidBody>(); for (auto ent : entsWantPhys) { auto* btComp = m_ecs->tryGet<BulletBody>(ent); if ( btComp != nullptr ) { @@ -117,20 +113,20 @@ namespace fggl::phys::bullet { } // set up the bullet proxy for our object - btComp = m_ecs->add<BulletBody>(ent); - const auto* fgBody = m_ecs->get<phys::RigidBody>(ent); + btComp = &m_ecs->add<BulletBody>(ent); + const auto& fgBody = m_ecs->get<phys::RigidBody>(ent); btComp->motion = new FgglMotionState(m_ecs, ent); // collisions btCollisionShape* colShape = shape_to_bullet(fgBody); btVector3 localInt(0, 0, 0); - if ( !fgBody->isStatic() ) { - colShape->calculateLocalInertia(fgBody->mass, localInt); + if ( !fgBody.isStatic() ) { + colShape->calculateLocalInertia(fgBody.mass, localInt); } // setup the construction information btRigidBody::btRigidBodyConstructionInfo bodyCI( - fgBody->mass, + fgBody.mass, btComp->motion, colShape, localInt @@ -139,12 +135,12 @@ namespace fggl::phys::bullet { btComp->body->setUserIndex( static_cast<int>(ent) ); - if (!fgBody->isStatic()) { + if (!fgBody.isStatic()) { btComp->body->setRestitution(0.5f); btComp->body->setRollingFriction(0.0f); } - if ( fgBody->type == BodyType::KINEMATIC ) { + if ( fgBody.type == BodyType::KINEMATIC ) { auto flags = btComp->body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT; btComp->body->setCollisionFlags( flags ); @@ -158,42 +154,42 @@ namespace fggl::phys::bullet { } void BulletPhysicsEngine::forceSyncToECS() { - const auto physEnts = m_ecs->findMatching<BulletBody>(); + const auto physEnts = m_ecs->find<math::Transform, BulletBody>(); for (const auto& ent : physEnts) { - auto* transform = m_ecs->get<math::Transform>(ent); - auto* physBody = m_ecs->get<BulletBody>(ent); + auto& transform = m_ecs->get<math::Transform>(ent); + auto& physBody = m_ecs->get<BulletBody>(ent); - if ( physBody->body->isKinematicObject() ) { + if ( physBody.body->isKinematicObject() ) { continue; } btTransform bTransform; - physBody->motion->getWorldTransform(bTransform); + physBody.motion->getWorldTransform(bTransform); // set calculate and set ecs position auto& btPos = bTransform.getOrigin(); math::vec3 pos{btPos.x(), btPos.y(), btPos.z()}; - transform->origin( pos ); + transform.origin( pos ); // set the rotation const auto& btRot = bTransform.getRotation(); math::vec3 rot; btRot.getEulerZYX(rot.x, rot.y, rot.z); - transform->euler(rot); + transform.euler(rot); } } BulletPhysicsEngine::~BulletPhysicsEngine() { // clean up the rigid bodies - auto entRB = m_ecs->findMatching<BulletBody>(); - for (auto& ent : entRB) { - auto* bulletBody = m_ecs->get<BulletBody>(ent); - m_world->removeCollisionObject(bulletBody->body); + auto entRB = m_ecs->find<BulletBody>(); + for (const auto& ent : entRB) { + auto& bulletBody = m_ecs->get<BulletBody>(ent); + m_world->removeCollisionObject(bulletBody.body); // release resources and delete - bulletBody->release(); - m_ecs->remove<BulletBody>(ent); + bulletBody.release(); + //m_ecs->remove<BulletBody>(ent); } // delete the world @@ -206,7 +202,7 @@ namespace fggl::phys::bullet { delete m_config.collisionConfiguration; } - static void handleCollisionCallbacks(ecs3::World* world, ecs3::entity_t owner, ecs3::entity_t other) { + static void handleCollisionCallbacks(entity::EntityManager* world, entity::EntityID owner, entity::EntityID other) { if ( !world->has<CollisionCallbacks>(owner) ) { return; } @@ -230,11 +226,11 @@ namespace fggl::phys::bullet { void BulletPhysicsEngine::pollCollisions() { // flush collision caches - auto caches = m_ecs->findMatching<CollisionCache>(); - for( auto& ent : caches) { - auto* cache = m_ecs->get<CollisionCache>(ent); - std::swap(cache->collisions, cache->lastFrame); - cache->collisions.clear(); + auto caches = m_ecs->find<CollisionCache>(); + for( const auto& ent : caches) { + auto& cache = m_ecs->get<CollisionCache>(ent); + std::swap(cache.collisions, cache.lastFrame); + cache.collisions.clear(); } // deal with the number of manifolds @@ -245,15 +241,6 @@ namespace fggl::phys::bullet { const auto* body0 = contactManifold->getBody0(); const auto* body1 = contactManifold->getBody1(); - // FIXME rigid body didn't die according to plan! - if ( body0->getUserIndex() == ecs::NULL_ENTITY || body1->getUserIndex() == ecs::NULL_ENTITY) { - continue; - } - - if ( !m_ecs->alive( body0->getUserIndex()) || !m_ecs->alive(body1->getUserIndex()) ) { - continue; - } - int numContacts = contactManifold->getNumContacts(); for ( auto contactIdx = 0; contactIdx < numContacts; ++contactIdx ) { auto& point = contactManifold->getContactPoint(contactIdx); @@ -264,14 +251,14 @@ namespace fggl::phys::bullet { } } - handleCollisionCallbacks(m_ecs, body0->getUserIndex(), body1->getUserIndex()); - handleCollisionCallbacks(m_ecs, body1->getUserIndex(), body0->getUserIndex()); + //handleCollisionCallbacks(m_ecs, body0->getUserIndex(), body1->getUserIndex()); + //handleCollisionCallbacks(m_ecs, body1->getUserIndex(), body0->getUserIndex()); } // note contacts that have ended - caches = m_ecs->findMatching<CollisionCache>(); // re-fetch, entities can (and probably do) die in handlers. - for( auto& ent : caches) { - auto* cache = m_ecs->get<CollisionCache>(ent); + caches = m_ecs->find<CollisionCache>(); // re-fetch, entities can (and probably do) die in handlers. + for( const auto& ent : caches) { + auto& cache = m_ecs->get<CollisionCache>(ent); // if we don't have an exit callback, it's pointless checking the cache auto* callbacks = m_ecs->tryGet<CollisionCallbacks>(ent); @@ -280,20 +267,20 @@ namespace fggl::phys::bullet { } // check if a collision has ended - for (const auto& other : cache->lastFrame) { - auto itr = cache->collisions.find(other); - if (itr == cache->collisions.end()) { + for (const auto& other : cache.lastFrame) { + auto itr = cache.collisions.find(other); + if (itr == cache.collisions.end()) { callbacks->onExit( ent,other); } } } } - void BulletPhysicsEngine::onEntityDeath(ecs::entity_t entity) { + void BulletPhysicsEngine::onEntityDeath(entity::EntityID entity) { auto* btPhysics = m_ecs->tryGet<BulletBody>(entity); if ( btPhysics != nullptr) { // decouple physics from entity - btPhysics->body->setUserIndex( ecs::NULL_ENTITY ); + //btPhysics->body->setUserIndex( entity::INVALID ); m_world->removeRigidBody( btPhysics->body ); btPhysics->release(); } @@ -311,7 +298,7 @@ namespace fggl::phys::bullet { m_contactCache.removed.push_back(point); } - ecs3::entity_t BulletPhysicsEngine::raycast(math::vec3 from, math::vec3 to) { + entity::EntityID BulletPhysicsEngine::raycast(math::vec3 from, math::vec3 to) { const auto btFrom = to_bullet(from); const auto btTo = to_bullet(to); @@ -319,18 +306,19 @@ namespace fggl::phys::bullet { m_world->rayTest(btFrom, btTo, rayTestResult); if ( !rayTestResult.hasHit() ) { - return ecs3::NULL_ENTITY; + return entity::INVALID; } // tell the user what it hit - return rayTestResult.m_collisionObject->getUserIndex(); + return entity::INVALID; +// return rayTestResult.m_collisionObject->getUserIndex(); } - std::vector<ContactPoint> BulletPhysicsEngine::scanCollisions(ecs3::entity_t entity) { + std::vector<ContactPoint> BulletPhysicsEngine::scanCollisions(entity::EntityID entity) { return std::vector<ContactPoint>(); } - std::vector<ecs3::entity_t> BulletPhysicsEngine::raycastAll(math::vec3 fromPos, math::vec3 toPos) { + std::vector<entity::EntityID> BulletPhysicsEngine::raycastAll(math::vec3 fromPos, math::vec3 toPos) { const auto btFrom = to_bullet(fromPos); const auto btTo = to_bullet(toPos); @@ -345,21 +333,21 @@ namespace fggl::phys::bullet { // hit processing const auto hits = rayTestResult.m_collisionObjects.size(); - std::vector<ecs3::entity_t> hitVec; + std::vector<entity::EntityID> hitVec; hitVec.reserve(hits); for ( auto i = 0; i < hits; ++i) { - ecs3::entity_t entity = rayTestResult.m_collisionObjects[i]->getUserIndex(); - hitVec.push_back(entity); + auto entity = rayTestResult.m_collisionObjects[i]->getUserIndex(); + //hitVec.push_back(entity); } return hitVec; } - std::vector<ecs3::entity_t> BulletPhysicsEngine::sweep(PhyShape &shape, + std::vector<entity::EntityID> BulletPhysicsEngine::sweep(PhyShape &shape, math::Transform &from, math::Transform &to) { - return std::vector<ecs3::entity_t>(); + return std::vector<entity::EntityID>(); } } // namespace fggl::phys::bullet \ No newline at end of file diff --git a/integrations/entt/CMakeLists.txt b/integrations/entt/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ed4cc62be5ae900a326bfc8f9c2690bd37894756 --- /dev/null +++ b/integrations/entt/CMakeLists.txt @@ -0,0 +1,5 @@ +target_include_directories( fggl + PUBLIC + $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include> + $<INSTALL_INTERFACE:include> +) diff --git a/integrations/entt/include/fggl/vendor/entt.hpp b/integrations/entt/include/fggl/vendor/entt.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f1523ae01d26c5380182977515d00162d8dce92a --- /dev/null +++ b/integrations/entt/include/fggl/vendor/entt.hpp @@ -0,0 +1,66129 @@ +// #include "config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "config/macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + +// #include "config/version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + +// #include "container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include <algorithm> +#include <cmath> +#include <cstddef> +#include <functional> +#include <iterator> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include <cstddef> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include <cstdint> +#include <type_traits> +// #include "../config/config.h" + + +namespace entt { + +template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)> +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type, std::size_t, typename = void> +struct compressed_pair_element { +using reference = Type &; +using const_reference = const Type &; + +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>> +compressed_pair_element() +: value{} {} + +template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>> +compressed_pair_element(Args &&args) +: value{std::forward<Args>(args)} {} + +template<typename... Args, std::size_t... Index> +compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) +: value{std::forward<Args>(std::get<Index>(args))...} {} + +[[nodiscard]] reference get() ENTT_NOEXCEPT { +return value; +} + +[[nodiscard]] const_reference get() const ENTT_NOEXCEPT { +return value; +} + +private: +Type value; +}; + +template<typename Type, std::size_t Tag> +struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type { +using reference = Type &; +using const_reference = const Type &; +using base_type = Type; + +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>> +compressed_pair_element() +: base_type{} {} + +template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>> +compressed_pair_element(Args &&args) +: base_type{std::forward<Args>(args)} {} + +template<typename... Args, std::size_t... Index> +compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) +: base_type{std::forward<Args>(std::get<Index>(args))...} {} + +[[nodiscard]] reference get() ENTT_NOEXCEPT { +return *this; +} + +[[nodiscard]] const_reference get() const ENTT_NOEXCEPT { +return *this; +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +class compressed_pair final +: internal::compressed_pair_element<First, 0u>, +internal::compressed_pair_element<Second, 1u> { +using first_base = internal::compressed_pair_element<First, 0u>; +using second_base = internal::compressed_pair_element<Second, 1u>; + +public: +/*! @brief The type of the first element that the pair stores. */ +using first_type = First; +/*! @brief The type of the second element that the pair stores. */ +using second_type = Second; + +/** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>> +constexpr compressed_pair() +: first_base{}, +second_base{} {} + +/** + * @brief Copy constructor. + * @param other The instance to copy from. + */ +constexpr compressed_pair(const compressed_pair &other) = default; + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +constexpr compressed_pair(compressed_pair &&other) = default; + +/** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ +template<typename Arg, typename Other> +constexpr compressed_pair(Arg &&arg, Other &&other) +: first_base{std::forward<Arg>(arg)}, +second_base{std::forward<Other>(other)} {} + +/** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ +template<typename... Args, typename... Other> +constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) +: first_base{std::move(args), std::index_sequence_for<Args...>{}}, +second_base{std::move(other), std::index_sequence_for<Other...>{}} {} + +/** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ +constexpr compressed_pair &operator=(const compressed_pair &other) = default; + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ +constexpr compressed_pair &operator=(compressed_pair &&other) = default; + +/** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ +[[nodiscard]] first_type &first() ENTT_NOEXCEPT { +return static_cast<first_base &>(*this).get(); +} + +/*! @copydoc first */ +[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { +return static_cast<const first_base &>(*this).get(); +} + +/** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ +[[nodiscard]] second_type &second() ENTT_NOEXCEPT { +return static_cast<second_base &>(*this).get(); +} + +/*! @copydoc second */ +[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { +return static_cast<const second_base &>(*this).get(); +} + +/** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ +void swap(compressed_pair &other) { +using std::swap; +swap(first(), other.first()); +swap(second(), other.second()); +} + +/** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ +template<std::size_t Index> +decltype(auto) get() ENTT_NOEXCEPT { +if constexpr(Index == 0u) { +return first(); +} else { +static_assert(Index == 1u, "Index out of bounds"); +return second(); +} +} + +/*! @copydoc get */ +template<std::size_t Index> +decltype(auto) get() const ENTT_NOEXCEPT { +if constexpr(Index == 0u) { +return first(); +} else { +static_assert(Index == 1u, "Index out of bounds"); +return second(); +} +} +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template<typename Type, typename Other> +compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template<typename First, typename Second> +inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) { +lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<size_t Index, typename First, typename Second> +struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> { +static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include <iterator> +#include <memory> +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template<typename Type> +struct input_iterator_pointer final { +/*! @brief Pointer type. */ +using pointer = Type *; + +/*! @brief Default copy constructor, deleted on purpose. */ +input_iterator_pointer(const input_iterator_pointer &) = delete; + +/*! @brief Default move constructor. */ +input_iterator_pointer(input_iterator_pointer &&) = default; + +/** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ +input_iterator_pointer(Type &&val) +: value{std::move(val)} {} + +/** + * @brief Default copy assignment operator, deleted on purpose. + * @return This proxy object. + */ +input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; + +/** + * @brief Default move assignment operator. + * @return This proxy object. + */ +input_iterator_pointer &operator=(input_iterator_pointer &&) = default; + +/** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ +[[nodiscard]] pointer operator->() ENTT_NOEXCEPT { +return std::addressof(value); +} + +private: +Type value; +}; + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template<typename It, typename Sentinel = It> +struct iterable_adaptor final { +/*! @brief Value type. */ +using value_type = typename std::iterator_traits<It>::value_type; +/*! @brief Iterator type. */ +using iterator = It; +/*! @brief Sentinel type. */ +using sentinel = Sentinel; + +/*! @brief Default constructor. */ +iterable_adaptor() = default; + +/** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ +iterable_adaptor(iterator from, sentinel to) +: first{from}, +last{to} {} + +/** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return first; +} + +/** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ +[[nodiscard]] sentinel end() const ENTT_NOEXCEPT { +return last; +} + +/*! @copydoc begin */ +[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { +return begin(); +} + +/*! @copydoc end */ +[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT { +return end(); +} + +private: +It first; +Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include <cstddef> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template<typename Type> +[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT { +if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) { +return ptr; +} else { +return to_address(std::forward<Type>(ptr).operator->()); +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) { +lhs = rhs; +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) { +lhs = std::move(rhs); +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); + +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) { +using std::swap; +swap(lhs, rhs); +} +} + +/** + * @brief Checks whether a value is a power of two or not. + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value. + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded"); +std::size_t curr = value - (value != 0u); + +for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) { +curr |= curr >> next; +} + +return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT { +ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); +return value & (mod - 1u); +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Args Types of arguments to use to construct the object. + */ +template<typename Allocator> +struct allocation_deleter: private Allocator { +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Pointer type. */ +using pointer = typename std::allocator_traits<Allocator>::pointer; + +/** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ +allocation_deleter(const allocator_type &alloc) +: Allocator{alloc} {} + +/** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ +void operator()(pointer ptr) { +using alloc_traits = typename std::allocator_traits<Allocator>; +alloc_traits::destroy(*this, to_address(ptr)); +alloc_traits::deallocate(*this, ptr, 1u); +} +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template<typename Type, typename Allocator, typename... Args> +auto allocate_unique(Allocator &allocator, Args &&...args) { +static_assert(!std::is_array_v<Type>, "Array types are not supported"); + +using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>; +using allocator_type = typename alloc_traits::allocator_type; + +allocator_type alloc{allocator}; +auto ptr = alloc_traits::allocate(alloc, 1u); + +ENTT_TRY { +alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...); +} +ENTT_CATCH { +alloc_traits::deallocate(alloc, ptr, 1u); +ENTT_THROW; +} + +return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc}; +} + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type> +struct uses_allocator_construction { +template<typename Allocator, typename... Params> +static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT { +if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) { +return std::forward_as_tuple(std::forward<Params>(params)...); +} else { +static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request"); + +if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) { +return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...); +} else { +static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request"); +return std::forward_as_tuple(std::forward<Params>(params)..., allocator); +} +} +} +}; + +template<typename Type, typename Other> +struct uses_allocator_construction<std::pair<Type, Other>> { +using type = std::pair<Type, Other>; + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT { +return std::make_tuple( +std::piecewise_construct, +std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)), +std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second))); +} + +template<typename Allocator> +static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second))); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT { +return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { +return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { +return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include <functional> +#include <memory> + +namespace entt { + +template< +typename Key, +typename Type, +typename = std::hash<Key>, +typename = std::equal_to<Key>, +typename = std::allocator<std::pair<const Key, Type>>> +class dense_map; + +template< +typename Type, +typename = std::hash<Type>, +typename = std::equal_to<Type>, +typename = std::allocator<Type>> +class dense_set; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Key, typename Type> +struct dense_map_node final { +using value_type = std::pair<Key, Type>; + +template<typename... Args> +dense_map_node(const std::size_t pos, Args &&...args) +: next{pos}, +element{std::forward<Args>(args)...} {} + +template<typename Allocator, typename... Args> +dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) +: next{pos}, +element{entt::make_obj_using_allocator<value_type>(allocator, std::forward<Args>(args)...)} {} + +template<typename Allocator> +dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) +: next{other.next}, +element{entt::make_obj_using_allocator<value_type>(allocator, other.element)} {} + +template<typename Allocator> +dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) +: next{other.next}, +element{entt::make_obj_using_allocator<value_type>(allocator, std::move(other.element))} {} + +std::size_t next; +value_type element; +}; + +template<typename It> +class dense_map_iterator final { +template<typename> +friend class dense_map_iterator; + +using first_type = decltype(std::as_const(std::declval<It>()->element.first)); +using second_type = decltype((std::declval<It>()->element.second)); + +public: +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; + +dense_map_iterator() ENTT_NOEXCEPT +: it{} {} + +dense_map_iterator(const It iter) ENTT_NOEXCEPT +: it{iter} {} + +template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> +dense_map_iterator(const dense_map_iterator<Other> &other) ENTT_NOEXCEPT +: it{other.it} {} + +dense_map_iterator &operator++() ENTT_NOEXCEPT { +return ++it, *this; +} + +dense_map_iterator operator++(int) ENTT_NOEXCEPT { +dense_map_iterator orig = *this; +return ++(*this), orig; +} + +dense_map_iterator &operator--() ENTT_NOEXCEPT { +return --it, *this; +} + +dense_map_iterator operator--(int) ENTT_NOEXCEPT { +dense_map_iterator orig = *this; +return operator--(), orig; +} + +dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { +it += value; +return *this; +} + +dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { +dense_map_iterator copy = *this; +return (copy += value); +} + +dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { +return (*this += -value); +} + +dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { +return (*this + -value); +} + +[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { +return {it[value].element.first, it[value].element.second}; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return {it->element.first, it->element.second}; +} + +template<typename ILhs, typename IRhs> +friend std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT; + +template<typename ILhs, typename IRhs> +friend bool operator==(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT; + +template<typename ILhs, typename IRhs> +friend bool operator<(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT; + +private: +It it; +}; + +template<typename ILhs, typename IRhs> +[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it - rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator==(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it == rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator!=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator<(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it < rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator>(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator<=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs > rhs); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator>=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +template<typename It> +class dense_map_local_iterator final { +template<typename> +friend class dense_map_local_iterator; + +using first_type = decltype(std::as_const(std::declval<It>()->element.first)); +using second_type = decltype((std::declval<It>()->element.second)); + +public: +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; + +dense_map_local_iterator() ENTT_NOEXCEPT +: it{}, +offset{} {} + +dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT +: it{iter}, +offset{pos} {} + +template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> +dense_map_local_iterator(const dense_map_local_iterator<Other> &other) ENTT_NOEXCEPT +: it{other.it}, +offset{other.offset} {} + +dense_map_local_iterator &operator++() ENTT_NOEXCEPT { +return offset = it[offset].next, *this; +} + +dense_map_local_iterator operator++(int) ENTT_NOEXCEPT { +dense_map_local_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return {it[offset].element.first, it[offset].element.second}; +} + +[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { +return offset; +} + +private: +It it; +std::size_t offset; +}; + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator==(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.index() == rhs.index(); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator!=(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Key, typename Type, typename Hash, typename KeyEqual, typename Allocator> +class dense_map { +static constexpr float default_threshold = 0.875f; +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>; +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>>; + +template<typename Other> +[[nodiscard]] std::size_t key_to_bucket(const Other &key) const ENTT_NOEXCEPT { +return fast_mod(sparse.second()(key), bucket_count()); +} + +template<typename Other> +[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { +for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { +if(packed.second()(it->first, key)) { +return begin() + static_cast<typename iterator::difference_type>(it.index()); +} +} + +return end(); +} + +template<typename Other> +[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { +for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { +if(packed.second()(it->first, key)) { +return cbegin() + static_cast<typename iterator::difference_type>(it.index()); +} +} + +return cend(); +} + +template<typename Other, typename... Args> +[[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { +const auto index = key_to_bucket(key); + +if(auto it = constrained_find(key, index); it != end()) { +return std::make_pair(it, false); +} + +packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward<Other>(key)), std::forward_as_tuple(std::forward<Args>(args)...)); +sparse.first()[index] = packed.first().size() - 1u; +rehash_if_required(); + +return std::make_pair(--end(), true); +} + +template<typename Other, typename Arg> +[[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { +const auto index = key_to_bucket(key); + +if(auto it = constrained_find(key, index); it != end()) { +it->second = std::forward<Arg>(value); +return std::make_pair(it, false); +} + +packed.first().emplace_back(sparse.first()[index], std::forward<Other>(key), std::forward<Arg>(value)); +sparse.first()[index] = packed.first().size() - 1u; +rehash_if_required(); + +return std::make_pair(--end(), true); +} + +void move_and_pop(const std::size_t pos) { +if(const auto last = size() - 1u; pos != last) { +packed.first()[pos] = std::move(packed.first().back()); +size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); +for(; *curr != last; curr = &packed.first()[*curr].next) {} +*curr = pos; +} + +packed.first().pop_back(); +} + +void rehash_if_required() { +if(size() > (bucket_count() * max_load_factor())) { +rehash(bucket_count() * 2u); +} +} + +public: +/*! @brief Key type of the container. */ +using key_type = Key; +/*! @brief Mapped type of the container. */ +using mapped_type = Type; +/*! @brief Key-value type of the container. */ +using value_type = std::pair<const Key, Type>; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Type of function to use to hash the keys. */ +using hasher = Hash; +/*! @brief Type of function to use to compare the keys for equality. */ +using key_equal = KeyEqual; +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Input iterator type. */ +using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>; +/*! @brief Constant input iterator type. */ +using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_iterator>; +/*! @brief Input iterator type. */ +using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::iterator>; +/*! @brief Constant input iterator type. */ +using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_iterator>; + +/*! @brief Default constructor. */ +dense_map() +: dense_map(minimum_capacity) {} + +/** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ +explicit dense_map(const allocator_type &allocator) +: dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param allocator The allocator to use. + */ +dense_map(const size_type bucket_count, const allocator_type &allocator) +: dense_map{bucket_count, hasher{}, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ +dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) +: dense_map{bucket_count, hash, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ +explicit dense_map(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) +: sparse{allocator, hash}, +packed{allocator, equal}, +threshold{default_threshold} { +rehash(bucket_count); +} + +/*! @brief Default copy constructor. */ +dense_map(const dense_map &) = default; + +/** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ +dense_map(const dense_map &other, const allocator_type &allocator) +: sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, +packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, +threshold{other.threshold} {} + +/*! @brief Default move constructor. */ +dense_map(dense_map &&) = default; + +/** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ +dense_map(dense_map &&other, const allocator_type &allocator) +: sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, +packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, +threshold{other.threshold} {} + +/** + * @brief Default copy assignment operator. + * @return This container. + */ +dense_map &operator=(const dense_map &) = default; + +/** + * @brief Default move assignment operator. + * @return This container. + */ +dense_map &operator=(dense_map &&) = default; + +/** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ +[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { +return sparse.first().get_allocator(); +} + +/** + * @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. + */ +[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { +return packed.first().begin(); +} + +/*! @copydoc cbegin */ +[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { +return cbegin(); +} + +/*! @copydoc begin */ +[[nodiscard]] iterator begin() ENTT_NOEXCEPT { +return packed.first().begin(); +} + +/** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the internal array. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the element following the last instance of the + * internal array. + */ +[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { +return packed.first().end(); +} + +/*! @copydoc cend */ +[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { +return cend(); +} + +/*! @copydoc end */ +[[nodiscard]] iterator end() ENTT_NOEXCEPT { +return packed.first().end(); +} + +/** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return packed.first().empty(); +} + +/** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return packed.first().size(); +} + +/*! @brief Clears the container. */ +void clear() ENTT_NOEXCEPT { +sparse.first().clear(); +packed.first().clear(); +rehash(0u); +} + +/** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +std::pair<iterator, bool> insert(const value_type &value) { +return insert_or_do_nothing(value.first, value.second); +} + +/*! @copydoc insert */ +std::pair<iterator, bool> insert(value_type &&value) { +return insert_or_do_nothing(std::move(value.first), std::move(value.second)); +} + +/** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ +template<typename Arg> +std::enable_if_t<std::is_constructible_v<value_type, Arg &&>, std::pair<iterator, bool>> +insert(Arg &&value) { +return insert_or_do_nothing(std::forward<Arg>(value).first, std::forward<Arg>(value).second); +} + +/** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ +template<typename It> +void insert(It first, It last) { +for(; first != last; ++first) { +insert(*first); +} +} + +/** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ +template<typename Arg> +std::pair<iterator, bool> insert_or_assign(const key_type &key, Arg &&value) { +return insert_or_overwrite(key, std::forward<Arg>(value)); +} + +/*! @copydoc insert_or_assign */ +template<typename Arg> +std::pair<iterator, bool> insert_or_assign(key_type &&key, Arg &&value) { +return insert_or_overwrite(std::move(key), std::forward<Arg>(value)); +} + +/** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +template<typename... Args> +std::pair<iterator, bool> emplace([[maybe_unused]] Args &&...args) { +if constexpr(sizeof...(Args) == 0u) { +return insert_or_do_nothing(key_type{}); +} else if constexpr(sizeof...(Args) == 1u) { +return insert_or_do_nothing(std::forward<Args>(args).first..., std::forward<Args>(args).second...); +} else if constexpr(sizeof...(Args) == 2u) { +return insert_or_do_nothing(std::forward<Args>(args)...); +} else { +auto &node = packed.first().emplace_back(packed.first().size(), std::forward<Args>(args)...); +const auto index = key_to_bucket(node.element.first); + +if(auto it = constrained_find(node.element.first, index); it != end()) { +packed.first().pop_back(); +return std::make_pair(it, false); +} + +std::swap(node.next, sparse.first()[index]); +rehash_if_required(); + +return std::make_pair(--end(), true); +} +} + +/** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +template<typename... Args> +std::pair<iterator, bool> try_emplace(const key_type &key, Args &&...args) { +return insert_or_do_nothing(key, std::forward<Args>(args)...); +} + +/*! @copydoc try_emplace */ +template<typename... Args> +std::pair<iterator, bool> try_emplace(key_type &&key, Args &&...args) { +return insert_or_do_nothing(std::move(key), std::forward<Args>(args)...); +} + +/** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ +iterator erase(const_iterator pos) { +const auto diff = pos - cbegin(); +erase(pos->first); +return begin() + diff; +} + +/** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ +iterator erase(const_iterator first, const_iterator last) { +const auto dist = first - cbegin(); + +for(auto from = last - cbegin(); from != dist; --from) { +erase(packed.first()[from - 1u].element.first); +} + +return (begin() + dist); +} + +/** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ +size_type erase(const key_type &key) { +for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) { +if(packed.second()(packed.first()[*curr].element.first, key)) { +const auto index = *curr; +*curr = packed.first()[*curr].next; +move_and_pop(index); +return 1u; +} +} + +return 0u; +} + +/** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ +void swap(dense_map &other) { +using std::swap; +swap(sparse, other.sparse); +swap(packed, other.packed); +swap(threshold, other.threshold); +} + +/** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ +[[nodiscard]] mapped_type &at(const key_type &key) { +auto it = find(key); +ENTT_ASSERT(it != end(), "Invalid key"); +return it->second; +} + +/*! @copydoc at */ +[[nodiscard]] const mapped_type &at(const key_type &key) const { +auto it = find(key); +ENTT_ASSERT(it != cend(), "Invalid key"); +return it->second; +} + +/** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ +[[nodiscard]] mapped_type &operator[](const key_type &key) { +return insert_or_do_nothing(key).first->second; +} + +/** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ +[[nodiscard]] mapped_type &operator[](key_type &&key) { +return insert_or_do_nothing(std::move(key)).first->second; +} + +/** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ +[[nodiscard]] iterator find(const key_type &key) { +return constrained_find(key, key_to_bucket(key)); +} + +/*! @copydoc find */ +[[nodiscard]] const_iterator find(const key_type &key) const { +return constrained_find(key, key_to_bucket(key)); +} + +/** + * @brief Finds an element with a key that compares _equivalent_ to a given + * value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>> +find(const Other &key) { +return constrained_find(key, key_to_bucket(key)); +} + +/*! @copydoc find */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>> +find(const Other &key) const { +return constrained_find(key, key_to_bucket(key)); +} + +/** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ +[[nodiscard]] bool contains(const key_type &key) const { +return (find(key) != cend()); +} + +/** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>> +contains(const Other &key) const { +return (find(key) != cend()); +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] const_local_iterator cbegin(const size_type index) const { +return {packed.first().begin(), sparse.first()[index]}; +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] const_local_iterator begin(const size_type index) const { +return cbegin(index); +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] local_iterator begin(const size_type index) { +return {packed.first().begin(), sparse.first()[index]}; +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { +return {packed.first().begin(), (std::numeric_limits<size_type>::max)()}; +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const { +return cend(index); +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { +return {packed.first().begin(), (std::numeric_limits<size_type>::max)()}; +} + +/** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ +[[nodiscard]] size_type bucket_count() const { +return sparse.first().size(); +} + +/** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ +[[nodiscard]] size_type max_bucket_count() const { +return sparse.first().max_size(); +} + +/** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ +[[nodiscard]] size_type bucket_size(const size_type index) const { +return static_cast<size_type>(std::distance(begin(index), end(index))); +} + +/** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ +[[nodiscard]] size_type bucket(const key_type &key) const { +return key_to_bucket(key); +} + +/** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ +[[nodiscard]] float load_factor() const { +return size() / static_cast<float>(bucket_count()); +} + +/** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ +[[nodiscard]] float max_load_factor() const { +return threshold; +} + +/** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ +void max_load_factor(const float value) { +ENTT_ASSERT(value > 0.f, "Invalid load factor"); +threshold = value; +rehash(0u); +} + +/** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param count New number of buckets. + */ +void rehash(const size_type count) { +auto value = (std::max)(count, minimum_capacity); +value = (std::max)(value, static_cast<size_type>(size() / max_load_factor())); + +if(const auto sz = next_power_of_two(value); sz != bucket_count()) { +sparse.first().resize(sz); +std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits<size_type>::max)()); + +for(size_type pos{}, last = size(); pos < last; ++pos) { +const auto index = key_to_bucket(packed.first()[pos].element.first); +packed.first()[pos].next = std::exchange(sparse.first()[index], pos); +} +} +} + +/** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param count New number of elements. + */ +void reserve(const size_type count) { +packed.first().reserve(count); +rehash(static_cast<size_type>(std::ceil(count / max_load_factor()))); +} + +/** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ +[[nodiscard]] hasher hash_function() const { +return sparse.second(); +} + +/** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ +[[nodiscard]] key_equal key_eq() const { +return packed.second(); +} + +private: +compressed_pair<sparse_container_type, hasher> sparse; +compressed_pair<packed_container_type, key_equal> packed; +float threshold; +}; + +} // namespace entt + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace std { + +template<typename Key, typename Value, typename Allocator> +struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator> +: std::true_type {}; + +} // namespace std + +/** + * Internal details not to be documented. + * @endcond + */ + +#endif + +// #include "container/dense_set.hpp" +#ifndef ENTT_CONTAINER_DENSE_SET_HPP +#define ENTT_CONTAINER_DENSE_SET_HPP + +#include <algorithm> +#include <cmath> +#include <cstddef> +#include <functional> +#include <iterator> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename It> +class dense_set_iterator final { +template<typename> +friend class dense_set_iterator; + +public: +using value_type = typename It::value_type::second_type; +using pointer = const value_type *; +using reference = const value_type &; +using difference_type = std::ptrdiff_t; +using iterator_category = std::random_access_iterator_tag; + +dense_set_iterator() ENTT_NOEXCEPT +: it{} {} + +dense_set_iterator(const It iter) ENTT_NOEXCEPT +: it{iter} {} + +template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> +dense_set_iterator(const dense_set_iterator<Other> &other) ENTT_NOEXCEPT +: it{other.it} {} + +dense_set_iterator &operator++() ENTT_NOEXCEPT { +return ++it, *this; +} + +dense_set_iterator operator++(int) ENTT_NOEXCEPT { +dense_set_iterator orig = *this; +return ++(*this), orig; +} + +dense_set_iterator &operator--() ENTT_NOEXCEPT { +return --it, *this; +} + +dense_set_iterator operator--(int) ENTT_NOEXCEPT { +dense_set_iterator orig = *this; +return operator--(), orig; +} + +dense_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { +it += value; +return *this; +} + +dense_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { +dense_set_iterator copy = *this; +return (copy += value); +} + +dense_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { +return (*this += -value); +} + +dense_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { +return (*this + -value); +} + +[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { +return it[value].second; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return std::addressof(it->second); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return *operator->(); +} + +template<typename ILhs, typename IRhs> +friend std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT; + +template<typename ILhs, typename IRhs> +friend bool operator==(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT; + +template<typename ILhs, typename IRhs> +friend bool operator<(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT; + +private: +It it; +}; + +template<typename ILhs, typename IRhs> +[[nodiscard]] std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it - rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator==(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it == rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator!=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator<(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it < rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator>(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator<=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs > rhs); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator>=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +template<typename It> +class dense_set_local_iterator final { +template<typename> +friend class dense_set_local_iterator; + +public: +using value_type = typename It::value_type::second_type; +using pointer = const value_type *; +using reference = const value_type &; +using difference_type = std::ptrdiff_t; +using iterator_category = std::forward_iterator_tag; + +dense_set_local_iterator() ENTT_NOEXCEPT +: it{}, +offset{} {} + +dense_set_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT +: it{iter}, +offset{pos} {} + +template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> +dense_set_local_iterator(const dense_set_local_iterator<Other> &other) ENTT_NOEXCEPT +: it{other.it}, +offset{other.offset} {} + +dense_set_local_iterator &operator++() ENTT_NOEXCEPT { +return offset = it[offset].first, *this; +} + +dense_set_local_iterator operator++(int) ENTT_NOEXCEPT { +dense_set_local_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return std::addressof(it[offset].second); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return *operator->(); +} + +[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { +return offset; +} + +private: +It it; +std::size_t offset; +}; + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator==(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.index() == rhs.index(); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator!=(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Associative container for unique objects of a given type. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on its hash. Elements with the same hash code + * appear in the same bucket. + * + * @tparam Type Value type of the associative container. + * @tparam Hash Type of function to use to hash the values. + * @tparam KeyEqual Type of function to use to compare the values for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Type, typename Hash, typename KeyEqual, typename Allocator> +class dense_set { +static constexpr float default_threshold = 0.875f; +static constexpr std::size_t minimum_capacity = 8u; + +using node_type = std::pair<std::size_t, Type>; +using alloc_traits = std::allocator_traits<Allocator>; +static_assert(std::is_same_v<typename alloc_traits::value_type, 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>>; + +template<typename Other> +[[nodiscard]] std::size_t value_to_bucket(const Other &value) const ENTT_NOEXCEPT { +return fast_mod(sparse.second()(value), bucket_count()); +} + +template<typename Other> +[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) { +for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { +if(packed.second()(*it, value)) { +return begin() + static_cast<typename iterator::difference_type>(it.index()); +} +} + +return end(); +} + +template<typename Other> +[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const { +for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { +if(packed.second()(*it, value)) { +return cbegin() + static_cast<typename iterator::difference_type>(it.index()); +} +} + +return cend(); +} + +template<typename Other> +[[nodiscard]] auto insert_or_do_nothing(Other &&value) { +const auto index = value_to_bucket(value); + +if(auto it = constrained_find(value, index); it != end()) { +return std::make_pair(it, false); +} + +packed.first().emplace_back(sparse.first()[index], std::forward<Other>(value)); +sparse.first()[index] = packed.first().size() - 1u; +rehash_if_required(); + +return std::make_pair(--end(), true); +} + +void move_and_pop(const std::size_t pos) { +if(const auto last = size() - 1u; pos != last) { +packed.first()[pos] = std::move(packed.first().back()); +size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); +for(; *curr != last; curr = &packed.first()[*curr].first) {} +*curr = pos; +} + +packed.first().pop_back(); +} + +void rehash_if_required() { +if(size() > (bucket_count() * max_load_factor())) { +rehash(bucket_count() * 2u); +} +} + +public: +/*! @brief Key type of the container. */ +using key_type = Type; +/*! @brief Value type of the container. */ +using value_type = Type; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Type of function to use to hash the elements. */ +using hasher = Hash; +/*! @brief Type of function to use to compare the elements for equality. */ +using key_equal = KeyEqual; +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Random access iterator type. */ +using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>; +/*! @brief Constant random access iterator type. */ +using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_iterator>; +/*! @brief Forward iterator type. */ +using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::iterator>; +/*! @brief Constant forward iterator type. */ +using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_iterator>; + +/*! @brief Default constructor. */ +dense_set() +: dense_set(minimum_capacity) {} + +/** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ +explicit dense_set(const allocator_type &allocator) +: dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param allocator The allocator to use. + */ +dense_set(const size_type bucket_count, const allocator_type &allocator) +: dense_set{bucket_count, hasher{}, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ +dense_set(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) +: dense_set{bucket_count, hash, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ +explicit dense_set(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) +: sparse{allocator, hash}, +packed{allocator, equal}, +threshold{default_threshold} { +rehash(bucket_count); +} + +/*! @brief Default copy constructor. */ +dense_set(const dense_set &) = default; + +/** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ +dense_set(const dense_set &other, const allocator_type &allocator) +: sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, +packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, +threshold{other.threshold} {} + +/*! @brief Default move constructor. */ +dense_set(dense_set &&) = default; + +/** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ +dense_set(dense_set &&other, const allocator_type &allocator) +: sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, +packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, +threshold{other.threshold} {} + +/** + * @brief Default copy assignment operator. + * @return This container. + */ +dense_set &operator=(const dense_set &) = default; + +/** + * @brief Default move assignment operator. + * @return This container. + */ +dense_set &operator=(dense_set &&) = default; + +/** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ +[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { +return sparse.first().get_allocator(); +} + +/** + * @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. + */ +[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { +return packed.first().begin(); +} + +/*! @copydoc cbegin */ +[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { +return cbegin(); +} + +/*! @copydoc begin */ +[[nodiscard]] iterator begin() ENTT_NOEXCEPT { +return packed.first().begin(); +} + +/** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the internal array. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the element following the last instance of the + * internal array. + */ +[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { +return packed.first().end(); +} + +/*! @copydoc cend */ +[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { +return cend(); +} + +/*! @copydoc end */ +[[nodiscard]] iterator end() ENTT_NOEXCEPT { +return packed.first().end(); +} + +/** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return packed.first().empty(); +} + +/** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return packed.first().size(); +} + +/*! @brief Clears the container. */ +void clear() ENTT_NOEXCEPT { +sparse.first().clear(); +packed.first().clear(); +rehash(0u); +} + +/** + * @brief Inserts an element into the container, if it does not exist. + * @param value An element to insert into the container. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +std::pair<iterator, bool> insert(const value_type &value) { +return insert_or_do_nothing(value); +} + +/*! @copydoc insert */ +std::pair<iterator, bool> insert(value_type &&value) { +return insert_or_do_nothing(std::move(value)); +} + +/** + * @brief Inserts elements into the container, if they do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ +template<typename It> +void insert(It first, It last) { +for(; first != last; ++first) { +insert(*first); +} +} + +/** + * @brief Constructs an element in-place, if it does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +template<typename... Args> +std::pair<iterator, bool> emplace(Args &&...args) { +if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, value_type>)) { +return insert_or_do_nothing(std::forward<Args>(args)...); +} else { +auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward<Args>(args)...)); +const auto index = value_to_bucket(node.second); + +if(auto it = constrained_find(node.second, index); it != end()) { +packed.first().pop_back(); +return std::make_pair(it, false); +} + +std::swap(node.first, sparse.first()[index]); +rehash_if_required(); + +return std::make_pair(--end(), true); +} +} + +/** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ +iterator erase(const_iterator pos) { +const auto diff = pos - cbegin(); +erase(*pos); +return begin() + diff; +} + +/** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ +iterator erase(const_iterator first, const_iterator last) { +const auto dist = first - cbegin(); + +for(auto from = last - cbegin(); from != dist; --from) { +erase(packed.first()[from - 1u].second); +} + +return (begin() + dist); +} + +/** + * @brief Removes the element associated with a given value. + * @param value Value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ +size_type erase(const value_type &value) { +for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].first) { +if(packed.second()(packed.first()[*curr].second, value)) { +const auto index = *curr; +*curr = packed.first()[*curr].first; +move_and_pop(index); +return 1u; +} +} + +return 0u; +} + +/** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ +void swap(dense_set &other) { +using std::swap; +swap(sparse, other.sparse); +swap(packed, other.packed); +swap(threshold, other.threshold); +} + +/** + * @brief Finds an element with a given value. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ +[[nodiscard]] iterator find(const value_type &value) { +return constrained_find(value, value_to_bucket(value)); +} + +/*! @copydoc find */ +[[nodiscard]] const_iterator find(const value_type &value) const { +return constrained_find(value, value_to_bucket(value)); +} + +/** + * @brief Finds an element that compares _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>> +find(const Other &value) { +return constrained_find(value, value_to_bucket(value)); +} + +/*! @copydoc find */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>> +find(const Other &value) const { +return constrained_find(value, value_to_bucket(value)); +} + +/** + * @brief Checks if the container contains an element with a given value. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ +[[nodiscard]] bool contains(const value_type &value) const { +return (find(value) != cend()); +} + +/** + * @brief Checks if the container contains an element that compares + * _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>> +contains(const Other &value) const { +return (find(value) != cend()); +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] const_local_iterator cbegin(const size_type index) const { +return {packed.first().begin(), sparse.first()[index]}; +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] const_local_iterator begin(const size_type index) const { +return cbegin(index); +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] local_iterator begin(const size_type index) { +return {packed.first().begin(), sparse.first()[index]}; +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { +return {packed.first().begin(), (std::numeric_limits<size_type>::max)()}; +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const { +return cend(index); +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { +return {packed.first().begin(), (std::numeric_limits<size_type>::max)()}; +} + +/** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ +[[nodiscard]] size_type bucket_count() const { +return sparse.first().size(); +} + +/** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ +[[nodiscard]] size_type max_bucket_count() const { +return sparse.first().max_size(); +} + +/** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ +[[nodiscard]] size_type bucket_size(const size_type index) const { +return static_cast<size_type>(std::distance(begin(index), end(index))); +} + +/** + * @brief Returns the bucket for a given element. + * @param value The value of the element to examine. + * @return The bucket for the given element. + */ +[[nodiscard]] size_type bucket(const value_type &value) const { +return value_to_bucket(value); +} + +/** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ +[[nodiscard]] float load_factor() const { +return size() / static_cast<float>(bucket_count()); +} + +/** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ +[[nodiscard]] float max_load_factor() const { +return threshold; +} + +/** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ +void max_load_factor(const float value) { +ENTT_ASSERT(value > 0.f, "Invalid load factor"); +threshold = value; +rehash(0u); +} + +/** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param count New number of buckets. + */ +void rehash(const size_type count) { +auto value = (std::max)(count, minimum_capacity); +value = (std::max)(value, static_cast<size_type>(size() / max_load_factor())); + +if(const auto sz = next_power_of_two(value); sz != bucket_count()) { +sparse.first().resize(sz); +std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits<size_type>::max)()); + +for(size_type pos{}, last = size(); pos < last; ++pos) { +const auto index = value_to_bucket(packed.first()[pos].second); +packed.first()[pos].first = std::exchange(sparse.first()[index], pos); +} +} +} + +/** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param count New number of elements. + */ +void reserve(const size_type count) { +packed.first().reserve(count); +rehash(static_cast<size_type>(std::ceil(count / max_load_factor()))); +} + +/** + * @brief Returns the function used to hash the elements. + * @return The function used to hash the elements. + */ +[[nodiscard]] hasher hash_function() const { +return sparse.second(); +} + +/** + * @brief Returns the function used to compare elements for equality. + * @return The function used to compare elements for equality. + */ +[[nodiscard]] key_equal key_eq() const { +return packed.second(); +} + +private: +compressed_pair<sparse_container_type, hasher> sparse; +compressed_pair<packed_container_type, key_equal> packed; +float threshold; +}; + +} // namespace entt + +#endif + +// #include "core/algorithm.hpp" +#ifndef ENTT_CORE_ALGORITHM_HPP +#define ENTT_CORE_ALGORITHM_HPP + +#include <algorithm> +#include <functional> +#include <iterator> +#include <utility> +#include <vector> +// #include "utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { +/*! @brief Indicates that this is a transparent function object. */ +using is_transparent = void; + +/** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ +template<class Type> +[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { +return std::forward<Type>(value); +} +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template<typename Type, typename Class> +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template<typename Func> +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template<class... Func> +struct overloaded: Func... { +using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template<class... Func> +overloaded(Func...) -> overloaded<Func...>; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template<class Func> +struct y_combinator { +/** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ +y_combinator(Func recursive) +: func{std::move(recursive)} {} + +/** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ +template<class... Args> +decltype(auto) operator()(Args &&...args) const { +return func(*this, std::forward<Args>(args)...); +} + +/*! @copydoc operator()() */ +template<class... Args> +decltype(auto) operator()(Args &&...args) { +return func(*this, std::forward<Args>(args)...); +} + +private: +Func func; +}; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Function object to wrap `std::sort` in a class type. + * + * Unfortunately, `std::sort` cannot be passed as template argument to a class + * template or a function template.<br/> + * This class fills the gap by wrapping some flavors of `std::sort` in a + * function object. + */ +struct std_sort { +/** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @tparam Args Types of arguments to forward to the sort function. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + * @param args Arguments to forward to the sort function, if any. + */ +template<typename It, typename Compare = std::less<>, typename... Args> +void operator()(It first, It last, Compare compare = Compare{}, Args &&...args) const { +std::sort(std::forward<Args>(args)..., std::move(first), std::move(last), std::move(compare)); +} +}; + +/*! @brief Function object for performing insertion sort. */ +struct insertion_sort { +/** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + */ +template<typename It, typename Compare = std::less<>> +void operator()(It first, It last, Compare compare = Compare{}) const { +if(first < last) { +for(auto it = first + 1; it < last; ++it) { +auto value = std::move(*it); +auto pre = it; + +for(; pre > first && compare(value, *(pre - 1)); --pre) { +*pre = std::move(*(pre - 1)); +} + +*pre = std::move(value); +} +} +} +}; + +/** + * @brief Function object for performing LSD radix sort. + * @tparam Bit Number of bits processed per pass. + * @tparam N Maximum number of bits to sort. + */ +template<std::size_t Bit, std::size_t N> +struct radix_sort { +static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass"); + +/** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given _getter_ to access the + * actual data to be sorted. + * + * This implementation is inspired by the online book + * [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort). + * + * @tparam It Type of random access iterator. + * @tparam Getter Type of _getter_ function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param getter A valid _getter_ function object. + */ +template<typename It, typename Getter = identity> +void operator()(It first, It last, Getter getter = Getter{}) const { +if(first < last) { +static constexpr auto mask = (1 << Bit) - 1; +static constexpr auto buckets = 1 << Bit; +static constexpr auto passes = N / Bit; + +using value_type = typename std::iterator_traits<It>::value_type; +std::vector<value_type> aux(std::distance(first, last)); + +auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) { +std::size_t index[buckets]{}; +std::size_t count[buckets]{}; + +for(auto it = from; it != to; ++it) { +++count[(getter(*it) >> start) & mask]; +} + +for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) { +index[pos + 1u] = index[pos] + count[pos]; +} + +for(auto it = from; it != to; ++it) { +out[index[(getter(*it) >> start) & mask]++] = std::move(*it); +} +}; + +for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) { +part(first, last, aux.begin(), pass * Bit); +part(aux.begin(), aux.end(), first, (pass + 1) * Bit); +} + +if constexpr(passes & 1) { +part(first, last, aux.begin(), (passes - 1) * Bit); +std::move(aux.begin(), aux.end(), first); +} +} +} +}; + +} // namespace entt + +#endif + +// #include "core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + +#include <cstddef> +#include <memory> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { +/*! @brief Indicates that this is a transparent function object. */ +using is_transparent = void; + +/** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ +template<class Type> +[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { +return std::forward<Type>(value); +} +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template<typename Type, typename Class> +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template<typename Func> +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template<class... Func> +struct overloaded: Func... { +using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template<class... Func> +overloaded(Func...) -> overloaded<Func...>; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template<class Func> +struct y_combinator { +/** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ +y_combinator(Func recursive) +: func{std::move(recursive)} {} + +/** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ +template<class... Args> +decltype(auto) operator()(Args &&...args) const { +return func(*this, std::forward<Args>(args)...); +} + +/*! @copydoc operator()() */ +template<class... Args> +decltype(auto) operator()(Args &&...args) { +return func(*this, std::forward<Args>(args)...); +} + +private: +Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include <cstdint> +#include <type_traits> +// #include "../config/config.h" + + +namespace entt { + +template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)> +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include <string_view> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#endif + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include <cstddef> +#include <cstdint> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename> +struct fnv1a_traits; + +template<> +struct fnv1a_traits<std::uint32_t> { +using type = std::uint32_t; +static constexpr std::uint32_t offset = 2166136261; +static constexpr std::uint32_t prime = 16777619; +}; + +template<> +struct fnv1a_traits<std::uint64_t> { +using type = std::uint64_t; +static constexpr std::uint64_t offset = 14695981039346656037ull; +static constexpr std::uint64_t prime = 1099511628211ull; +}; + +template<typename Char> +struct basic_hashed_string { +using value_type = Char; +using size_type = std::size_t; +using hash_type = id_type; + +const value_type *repr; +size_type length; +hash_type hash; +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers in the codebase while using their numeric + * counterparts at runtime.<br/> + * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +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>; + +struct const_wrapper { +// non-explicit constructor on purpose +constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {} +const Char *repr; +}; + +// Fowler–Noll–Vo hash function v. 1a - the good +[[nodiscard]] static constexpr auto helper(const Char *str) ENTT_NOEXCEPT { +base_type base{str, 0u, hs_traits::offset}; + +for(; str[base.length]; ++base.length) { +base.hash = (base.hash ^ static_cast<hs_traits::type>(str[base.length])) * hs_traits::prime; +} + +return base; +} + +// Fowler–Noll–Vo hash function v. 1a - the good +[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) ENTT_NOEXCEPT { +base_type base{str, len, hs_traits::offset}; + +for(size_type pos{}; pos < len; ++pos) { +base.hash = (base.hash ^ static_cast<hs_traits::type>(str[pos])) * hs_traits::prime; +} + +return base; +} + +public: +/*! @brief Character type. */ +using value_type = typename base_type::value_type; +/*! @brief Unsigned integer type. */ +using size_type = typename base_type::size_type; +/*! @brief Unsigned integer type. */ +using hash_type = typename base_type::hash_type; + +/** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ +[[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) ENTT_NOEXCEPT { +return basic_hashed_string{str, len}; +} + +/** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ +template<std::size_t N> +[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { +return basic_hashed_string{str}; +} + +/** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ +[[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { +return basic_hashed_string{wrapper}; +} + +/*! @brief Constructs an empty hashed string. */ +constexpr basic_hashed_string() ENTT_NOEXCEPT +: base_type{} {} + +/** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +constexpr basic_hashed_string(const value_type *str, const size_type len) ENTT_NOEXCEPT +: base_type{helper(str, len)} {} + +/** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template<std::size_t N> +constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT +: base_type{helper(str)} {} + +/** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ +explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT +: base_type{helper(wrapper.repr)} {} + +/** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ +[[nodiscard]] constexpr size_type size() const ENTT_NOEXCEPT { +return base_type::length; +} + +/** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ +[[nodiscard]] constexpr const value_type *data() const ENTT_NOEXCEPT { +return base_type::repr; +} + +/** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ +[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { +return base_type::hash; +} + +/*! @copydoc data */ +[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { +return data(); +} + +/** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ +[[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template<typename Char> +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template<typename Char, std::size_t N> +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>; + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string<char>; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string<wchar_t>; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT { +return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { +return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +struct ENTT_API type_index final { +[[nodiscard]] static id_type next() ENTT_NOEXCEPT { +static ENTT_MAYBE_ATOMIC(id_type) value{}; +return value++; +} +}; + +template<typename Type> +[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION +std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; +auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); +auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); +return value; +#else +return std::string_view{""}; +#endif +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { +constexpr auto value = stripped_type_name<Type>(); +return value; +} + +template<typename Type> +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { +static const auto value = stripped_type_name<Type>(); +return value; +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { +constexpr auto stripped = stripped_type_name<Type>(); +constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); +return value; +} + +template<typename Type> +[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { +static const auto value = [](const auto stripped) { +return hashed_string::value(stripped.data(), stripped.size()); +}(stripped_type_name<Type>()); +return value; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template<typename Type, typename = void> +struct ENTT_API type_index final { +/** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ +[[nodiscard]] static id_type value() ENTT_NOEXCEPT { +static const id_type value = internal::type_index::next(); +return value; +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template<typename Type, typename = void> +struct type_hash final { +/** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION +[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { +return internal::type_hash<Type>(0); +#else +[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { +return type_index<Type>::value(); +#endif +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template<typename Type, typename = void> +struct type_name final { +/** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ +[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { +return internal::type_name<Type>(0); +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { +return value(); +} +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { +/** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ +template<typename Type> +constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT +: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, +identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, +alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {} + +/** + * @brief Type index. + * @return Type index. + */ +[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT { +return seq; +} + +/** + * @brief Type hash. + * @return Type hash. + */ +[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { +return identifier; +} + +/** + * @brief Type name. + * @return Type name. + */ +[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT { +return alias; +} + +private: +id_type seq; +id_type identifier; +std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.<br/> + * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template<typename Type> +[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT { +if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) { +static type_info instance{std::in_place_type<Type>}; +return instance; +} else { +return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} +} + +/*! @copydoc type_id */ +template<typename Type> +[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT { +return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template<std::size_t Len, std::size_t Align> +class basic_any { +enum class operation : std::uint8_t { +copy, +move, +transfer, +assign, +destroy, +compare, +get +}; + +enum class policy : std::uint8_t { +owner, +ref, +cref +}; + +using storage_type = std::aligned_storage_t<Len + !Len, Align>; +using vtable_type = const void *(const operation, const basic_any &, const void *); + +template<typename Type> +static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>; + +template<typename Type> +static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] const void *other) { +static_assert(!std::is_same_v<Type, void> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type"); +const Type *element = nullptr; + +if constexpr(in_situ<Type>) { +element = value.owner() ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance); +} else { +element = static_cast<const Type *>(value.instance); +} + +switch(op) { +case operation::copy: +if constexpr(std::is_copy_constructible_v<Type>) { +static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element); +} +break; +case operation::move: +if constexpr(in_situ<Type>) { +if(value.owner()) { +return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))}; +} +} + +return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr)); +case operation::transfer: +if constexpr(std::is_move_assignable_v<Type>) { +*const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other))); +return other; +} +[[fallthrough]]; +case operation::assign: +if constexpr(std::is_copy_assignable_v<Type>) { +*const_cast<Type *>(element) = *static_cast<const Type *>(other); +return other; +} +break; +case operation::destroy: +if constexpr(in_situ<Type>) { +element->~Type(); +} else if constexpr(std::is_array_v<Type>) { +delete[] element; +} else { +delete element; +} +break; +case operation::compare: +if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) { +return *static_cast<const Type *>(element) == *static_cast<const Type *>(other) ? other : nullptr; +} else { +return (element == other) ? other : nullptr; +} +case operation::get: +return element; +} + +return nullptr; +} + +template<typename Type, typename... Args> +void initialize([[maybe_unused]] Args &&...args) { +if constexpr(!std::is_void_v<Type>) { +info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +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"); +mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref; +instance = (std::addressof(args), ...); +} else if constexpr(in_situ<Type>) { +if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) { +new(&storage) Type{std::forward<Args>(args)...}; +} else { +new(&storage) Type(std::forward<Args>(args)...); +} +} else { +if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) { +instance = new Type{std::forward<Args>(args)...}; +} else { +instance = new Type(std::forward<Args>(args)...); +} +} +} +} + +basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT +: instance{other.data()}, +info{other.info}, +vtable{other.vtable}, +mode{pol} {} + +public: +/*! @brief Size of the internal storage. */ +static constexpr auto length = Len; +/*! @brief Alignment requirement. */ +static constexpr auto alignment = Align; + +/*! @brief Default constructor. */ +constexpr basic_any() ENTT_NOEXCEPT +: instance{}, +info{&type_id<void>()}, +vtable{}, +mode{policy::owner} {} + +/** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ +template<typename Type, typename... Args> +explicit basic_any(std::in_place_type_t<Type>, Args &&...args) +: basic_any{} { +initialize<Type>(std::forward<Args>(args)...); +} + +/** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ +template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>> +basic_any(Type &&value) +: basic_any{} { +initialize<std::decay_t<Type>>(std::forward<Type>(value)); +} + +/** + * @brief Copy constructor. + * @param other The instance to copy from. + */ +basic_any(const basic_any &other) +: basic_any{} { +if(other.vtable) { +other.vtable(operation::copy, other, this); +} +} + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +basic_any(basic_any &&other) ENTT_NOEXCEPT +: instance{}, +info{other.info}, +vtable{other.vtable}, +mode{other.mode} { +if(other.vtable) { +other.vtable(operation::move, other, this); +} +} + +/*! @brief Frees the internal storage, whatever it means. */ +~basic_any() { +if(vtable && owner()) { +vtable(operation::destroy, *this, nullptr); +} +} + +/** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ +basic_any &operator=(const basic_any &other) { +reset(); + +if(other.vtable) { +other.vtable(operation::copy, other, this); +} + +return *this; +} + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This any object. + */ +basic_any &operator=(basic_any &&other) ENTT_NOEXCEPT { +reset(); + +if(other.vtable) { +other.vtable(operation::move, other, this); +info = other.info; +vtable = other.vtable; +mode = other.mode; +} + +return *this; +} + +/** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This any object. + */ +template<typename Type> +std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>, basic_any &> +operator=(Type &&value) { +emplace<std::decay_t<Type>>(std::forward<Type>(value)); +return *this; +} + +/** + * @brief Returns the object type if any, `type_id<void>()` otherwise. + * @return The object type if any, `type_id<void>()` otherwise. + */ +[[nodiscard]] const type_info &type() const ENTT_NOEXCEPT { +return *info; +} + +/** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ +[[nodiscard]] const void *data() const ENTT_NOEXCEPT { +return vtable ? vtable(operation::get, *this, nullptr) : nullptr; +} + +/** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ +[[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT { +return *info == req ? data() : nullptr; +} + +/** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ +[[nodiscard]] void *data() ENTT_NOEXCEPT { +return (!vtable || mode == policy::cref) ? nullptr : const_cast<void *>(vtable(operation::get, *this, nullptr)); +} + +/** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ +[[nodiscard]] void *data(const type_info &req) ENTT_NOEXCEPT { +return *info == req ? data() : nullptr; +} + +/** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ +template<typename Type, typename... Args> +void emplace(Args &&...args) { +reset(); +initialize<Type>(std::forward<Args>(args)...); +} + +/** + * @brief Assigns a value to the contained object without replacing it. + * @param other The value to assign to the contained object. + * @return True in case of success, false otherwise. + */ +bool assign(const any &other) { +if(vtable && mode != policy::cref && *info == *other.info) { +return (vtable(operation::assign, *this, other.data()) != nullptr); +} + +return false; +} + +/*! @copydoc assign */ +bool assign(any &&other) { +if(vtable && mode != policy::cref && *info == *other.info) { +if(auto *val = other.data(); val) { +return (vtable(operation::transfer, *this, val) != nullptr); +} else { +return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr); +} +} + +return false; +} + +/*! @brief Destroys contained object */ +void reset() { +if(vtable && owner()) { +vtable(operation::destroy, *this, nullptr); +} + +info = &type_id<void>(); +vtable = nullptr; +mode = policy::owner; +} + +/** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return vtable != nullptr; +} + +/** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ +bool operator==(const basic_any &other) const ENTT_NOEXCEPT { +if(vtable && *info == *other.info) { +return (vtable(operation::compare, *this, other.data()) != nullptr); +} + +return (!vtable && !other.vtable); +} + +/** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ +[[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT { +return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)}; +} + +/*! @copydoc as_ref */ +[[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT { +return basic_any{*this, policy::cref}; +} + +/** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ +[[nodiscard]] bool owner() const ENTT_NOEXCEPT { +return (mode == policy::owner); +} + +private: +union { +const void *instance; +storage_type storage; +}; +const type_info *info; +vtable_type *vtable; +policy mode; +}; + +/** + * @brief Checks if two wrappers differ in their content. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param lhs A wrapper, either empty or not. + * @param rhs A wrapper, either empty or not. + * @return True if the two wrappers differ in their content, false otherwise. + */ +template<std::size_t Len, std::size_t Align> +[[nodiscard]] inline bool operator!=(const basic_any<Len, Align> &lhs, const basic_any<Len, Align> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template<typename Type, std::size_t Len, std::size_t Align> +Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT { +const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); +ENTT_ASSERT(instance, "Invalid instance"); +return static_cast<Type>(*instance); +} + +/*! @copydoc any_cast */ +template<typename Type, std::size_t Len, std::size_t Align> +Type any_cast(basic_any<Len, Align> &data) ENTT_NOEXCEPT { +// forces const on non-reference types to make them work also with wrappers for const references +auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data); +ENTT_ASSERT(instance, "Invalid instance"); +return static_cast<Type>(*instance); +} + +/*! @copydoc any_cast */ +template<typename Type, std::size_t Len, std::size_t Align> +Type any_cast(basic_any<Len, Align> &&data) ENTT_NOEXCEPT { +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)); +} else { +return any_cast<Type>(data); +} +} else { +auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); +ENTT_ASSERT(instance, "Invalid instance"); +return static_cast<Type>(std::move(*instance)); +} +} + +/*! @copydoc any_cast */ +template<typename Type, std::size_t Len, std::size_t Align> +const Type *any_cast(const basic_any<Len, Align> *data) ENTT_NOEXCEPT { +const auto &info = type_id<std::remove_cv_t<std::remove_reference_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) ENTT_NOEXCEPT { +const auto &info = type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +// last attempt to make wrappers for const references return their values +return static_cast<Type *>(static_cast<constness_as_t<basic_any<Len, Align>, Type> *>(data)->data(info)); +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @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) { +return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @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) { +return basic_any<Len, Align>{std::in_place_type<std::conditional_t<std::is_rvalue_reference_v<Type>, std::decay_t<Type>, Type>>, std::forward<Type>(value)}; +} + +} // namespace entt + +#endif + +// #include "core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#endif + +// #include "core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include <cstddef> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "type_traits.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type, std::size_t, typename = void> +struct compressed_pair_element { +using reference = Type &; +using const_reference = const Type &; + +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>> +compressed_pair_element() +: value{} {} + +template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>> +compressed_pair_element(Args &&args) +: value{std::forward<Args>(args)} {} + +template<typename... Args, std::size_t... Index> +compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) +: value{std::forward<Args>(std::get<Index>(args))...} {} + +[[nodiscard]] reference get() ENTT_NOEXCEPT { +return value; +} + +[[nodiscard]] const_reference get() const ENTT_NOEXCEPT { +return value; +} + +private: +Type value; +}; + +template<typename Type, std::size_t Tag> +struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type { +using reference = Type &; +using const_reference = const Type &; +using base_type = Type; + +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>> +compressed_pair_element() +: base_type{} {} + +template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>> +compressed_pair_element(Args &&args) +: base_type{std::forward<Args>(args)} {} + +template<typename... Args, std::size_t... Index> +compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) +: base_type{std::forward<Args>(std::get<Index>(args))...} {} + +[[nodiscard]] reference get() ENTT_NOEXCEPT { +return *this; +} + +[[nodiscard]] const_reference get() const ENTT_NOEXCEPT { +return *this; +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +class compressed_pair final +: internal::compressed_pair_element<First, 0u>, +internal::compressed_pair_element<Second, 1u> { +using first_base = internal::compressed_pair_element<First, 0u>; +using second_base = internal::compressed_pair_element<Second, 1u>; + +public: +/*! @brief The type of the first element that the pair stores. */ +using first_type = First; +/*! @brief The type of the second element that the pair stores. */ +using second_type = Second; + +/** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>> +constexpr compressed_pair() +: first_base{}, +second_base{} {} + +/** + * @brief Copy constructor. + * @param other The instance to copy from. + */ +constexpr compressed_pair(const compressed_pair &other) = default; + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +constexpr compressed_pair(compressed_pair &&other) = default; + +/** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ +template<typename Arg, typename Other> +constexpr compressed_pair(Arg &&arg, Other &&other) +: first_base{std::forward<Arg>(arg)}, +second_base{std::forward<Other>(other)} {} + +/** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ +template<typename... Args, typename... Other> +constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) +: first_base{std::move(args), std::index_sequence_for<Args...>{}}, +second_base{std::move(other), std::index_sequence_for<Other...>{}} {} + +/** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ +constexpr compressed_pair &operator=(const compressed_pair &other) = default; + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ +constexpr compressed_pair &operator=(compressed_pair &&other) = default; + +/** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ +[[nodiscard]] first_type &first() ENTT_NOEXCEPT { +return static_cast<first_base &>(*this).get(); +} + +/*! @copydoc first */ +[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { +return static_cast<const first_base &>(*this).get(); +} + +/** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ +[[nodiscard]] second_type &second() ENTT_NOEXCEPT { +return static_cast<second_base &>(*this).get(); +} + +/*! @copydoc second */ +[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { +return static_cast<const second_base &>(*this).get(); +} + +/** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ +void swap(compressed_pair &other) { +using std::swap; +swap(first(), other.first()); +swap(second(), other.second()); +} + +/** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ +template<std::size_t Index> +decltype(auto) get() ENTT_NOEXCEPT { +if constexpr(Index == 0u) { +return first(); +} else { +static_assert(Index == 1u, "Index out of bounds"); +return second(); +} +} + +/*! @copydoc get */ +template<std::size_t Index> +decltype(auto) get() const ENTT_NOEXCEPT { +if constexpr(Index == 0u) { +return first(); +} else { +static_assert(Index == 1u, "Index out of bounds"); +return second(); +} +} +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template<typename Type, typename Other> +compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template<typename First, typename Second> +inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) { +lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<size_t Index, typename First, typename Second> +struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> { +static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "core/enum.hpp" +#ifndef ENTT_CORE_ENUM_HPP +#define ENTT_CORE_ENUM_HPP + +#include <type_traits> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Enable bitmask support for enum classes. + * @tparam Type The enum type for which to enable bitmask support. + */ +template<typename Type, typename = void> +struct enum_as_bitmask: std::false_type {}; + +/*! @copydoc enum_as_bitmask */ +template<typename Type> +struct enum_as_bitmask<Type, std::void_t<decltype(Type::_entt_enum_as_bitmask)>>: std::is_enum<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The enum class type for which to enable bitmask support. + */ +template<typename Type> +inline constexpr bool enum_as_bitmask_v = enum_as_bitmask<Type>::value; + +} // namespace entt + +/** + * @brief Operator available for enums for which bitmask support is enabled. + * @tparam Type Enum class type. + * @param lhs The first value to use. + * @param rhs The second value to use. + * @return The result of invoking the operator on the underlying types of the + * two values provided. + */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type> +operator|(const Type lhs, const Type rhs) ENTT_NOEXCEPT { +return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) | static_cast<std::underlying_type_t<Type>>(rhs)); +} + +/*! @copydoc operator| */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type> +operator&(const Type lhs, const Type rhs) ENTT_NOEXCEPT { +return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) & static_cast<std::underlying_type_t<Type>>(rhs)); +} + +/*! @copydoc operator| */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type> +operator^(const Type lhs, const Type rhs) ENTT_NOEXCEPT { +return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) ^ static_cast<std::underlying_type_t<Type>>(rhs)); +} + +/** + * @brief Operator available for enums for which bitmask support is enabled. + * @tparam Type Enum class type. + * @param value The value to use. + * @return The result of invoking the operator on the underlying types of the + * value provided. + */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type> +operator~(const Type value) ENTT_NOEXCEPT { +return static_cast<Type>(~static_cast<std::underlying_type_t<Type>>(value)); +} + +/*! @copydoc operator~ */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, bool> +operator!(const Type value) ENTT_NOEXCEPT { +return !static_cast<std::underlying_type_t<Type>>(value); +} + +/*! @copydoc operator| */ +template<typename Type> +constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &> +operator|=(Type &lhs, const Type rhs) ENTT_NOEXCEPT { +return (lhs = (lhs | rhs)); +} + +/*! @copydoc operator| */ +template<typename Type> +constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &> +operator&=(Type &lhs, const Type rhs) ENTT_NOEXCEPT { +return (lhs = (lhs & rhs)); +} + +/*! @copydoc operator| */ +template<typename Type> +constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &> +operator^=(Type &lhs, const Type rhs) ENTT_NOEXCEPT { +return (lhs = (lhs ^ rhs)); +} + +#endif + +// #include "core/family.hpp" +#ifndef ENTT_CORE_FAMILY_HPP +#define ENTT_CORE_FAMILY_HPP + +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Dynamic identifier generator. + * + * Utility class template that can be used to assign unique identifiers to types + * at runtime. Use different specializations to create separate sets of + * identifiers. + */ +template<typename...> +class family { +inline static ENTT_MAYBE_ATOMIC(id_type) identifier{}; + +public: +/*! @brief Unsigned integer type. */ +using family_type = id_type; + +/*! @brief Statically generated unique identifier for the given type. */ +template<typename... Type> +// at the time I'm writing, clang crashes during compilation if auto is used instead of family_type +inline static const family_type type = identifier++; +}; + +} // namespace entt + +#endif + +// #include "core/hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include <cstddef> +#include <cstdint> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename> +struct fnv1a_traits; + +template<> +struct fnv1a_traits<std::uint32_t> { +using type = std::uint32_t; +static constexpr std::uint32_t offset = 2166136261; +static constexpr std::uint32_t prime = 16777619; +}; + +template<> +struct fnv1a_traits<std::uint64_t> { +using type = std::uint64_t; +static constexpr std::uint64_t offset = 14695981039346656037ull; +static constexpr std::uint64_t prime = 1099511628211ull; +}; + +template<typename Char> +struct basic_hashed_string { +using value_type = Char; +using size_type = std::size_t; +using hash_type = id_type; + +const value_type *repr; +size_type length; +hash_type hash; +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers in the codebase while using their numeric + * counterparts at runtime.<br/> + * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +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>; + +struct const_wrapper { +// non-explicit constructor on purpose +constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {} +const Char *repr; +}; + +// Fowler–Noll–Vo hash function v. 1a - the good +[[nodiscard]] static constexpr auto helper(const Char *str) ENTT_NOEXCEPT { +base_type base{str, 0u, hs_traits::offset}; + +for(; str[base.length]; ++base.length) { +base.hash = (base.hash ^ static_cast<hs_traits::type>(str[base.length])) * hs_traits::prime; +} + +return base; +} + +// Fowler–Noll–Vo hash function v. 1a - the good +[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) ENTT_NOEXCEPT { +base_type base{str, len, hs_traits::offset}; + +for(size_type pos{}; pos < len; ++pos) { +base.hash = (base.hash ^ static_cast<hs_traits::type>(str[pos])) * hs_traits::prime; +} + +return base; +} + +public: +/*! @brief Character type. */ +using value_type = typename base_type::value_type; +/*! @brief Unsigned integer type. */ +using size_type = typename base_type::size_type; +/*! @brief Unsigned integer type. */ +using hash_type = typename base_type::hash_type; + +/** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ +[[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) ENTT_NOEXCEPT { +return basic_hashed_string{str, len}; +} + +/** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ +template<std::size_t N> +[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { +return basic_hashed_string{str}; +} + +/** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ +[[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { +return basic_hashed_string{wrapper}; +} + +/*! @brief Constructs an empty hashed string. */ +constexpr basic_hashed_string() ENTT_NOEXCEPT +: base_type{} {} + +/** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +constexpr basic_hashed_string(const value_type *str, const size_type len) ENTT_NOEXCEPT +: base_type{helper(str, len)} {} + +/** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template<std::size_t N> +constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT +: base_type{helper(str)} {} + +/** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ +explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT +: base_type{helper(wrapper.repr)} {} + +/** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ +[[nodiscard]] constexpr size_type size() const ENTT_NOEXCEPT { +return base_type::length; +} + +/** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ +[[nodiscard]] constexpr const value_type *data() const ENTT_NOEXCEPT { +return base_type::repr; +} + +/** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ +[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { +return base_type::hash; +} + +/*! @copydoc data */ +[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { +return data(); +} + +/** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ +[[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template<typename Char> +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template<typename Char, std::size_t N> +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>; + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string<char>; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string<wchar_t>; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT { +return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { +return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + +// #include "core/ident.hpp" +#ifndef ENTT_CORE_IDENT_HPP +#define ENTT_CORE_IDENT_HPP + +#include <cstddef> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" + +// #include "type_traits.hpp" + + +namespace entt { + +/** + * @brief Types identifiers. + * + * Variable template used to generate identifiers at compile-time for the given + * types. Use the `get` member function to know what's the identifier associated + * to the specific type. + * + * @note + * Identifiers are constant expression and can be used in any context where such + * an expression is required. As an example: + * @code{.cpp} + * using id = entt::identifier<a_type, another_type>; + * + * switch(a_type_identifier) { + * case id::type<a_type>: + * // ... + * break; + * case id::type<another_type>: + * // ... + * break; + * default: + * // ... + * } + * @endcode + * + * @tparam Types List of types for which to generate identifiers. + */ +template<typename... Types> +class identifier { +template<typename Type, std::size_t... Index> +[[nodiscard]] static constexpr id_type get(std::index_sequence<Index...>) ENTT_NOEXCEPT { +static_assert((std::is_same_v<Type, Types> || ...), "Invalid type"); +return (0 + ... + (std::is_same_v<Type, type_list_element_t<Index, type_list<std::decay_t<Types>...>>> ? id_type{Index} : id_type{})); +} + +public: +/*! @brief Unsigned integer type. */ +using identifier_type = id_type; + +/*! @brief Statically generated unique identifier for the given type. */ +template<typename Type> +static constexpr identifier_type type = get<std::decay_t<Type>>(std::index_sequence_for<Types...>{}); +}; + +} // namespace entt + +#endif + +// #include "core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include <iterator> +#include <memory> +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template<typename Type> +struct input_iterator_pointer final { +/*! @brief Pointer type. */ +using pointer = Type *; + +/*! @brief Default copy constructor, deleted on purpose. */ +input_iterator_pointer(const input_iterator_pointer &) = delete; + +/*! @brief Default move constructor. */ +input_iterator_pointer(input_iterator_pointer &&) = default; + +/** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ +input_iterator_pointer(Type &&val) +: value{std::move(val)} {} + +/** + * @brief Default copy assignment operator, deleted on purpose. + * @return This proxy object. + */ +input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; + +/** + * @brief Default move assignment operator. + * @return This proxy object. + */ +input_iterator_pointer &operator=(input_iterator_pointer &&) = default; + +/** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ +[[nodiscard]] pointer operator->() ENTT_NOEXCEPT { +return std::addressof(value); +} + +private: +Type value; +}; + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template<typename It, typename Sentinel = It> +struct iterable_adaptor final { +/*! @brief Value type. */ +using value_type = typename std::iterator_traits<It>::value_type; +/*! @brief Iterator type. */ +using iterator = It; +/*! @brief Sentinel type. */ +using sentinel = Sentinel; + +/*! @brief Default constructor. */ +iterable_adaptor() = default; + +/** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ +iterable_adaptor(iterator from, sentinel to) +: first{from}, +last{to} {} + +/** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return first; +} + +/** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ +[[nodiscard]] sentinel end() const ENTT_NOEXCEPT { +return last; +} + +/*! @copydoc begin */ +[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { +return begin(); +} + +/*! @copydoc end */ +[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT { +return end(); +} + +private: +It first; +Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include <cstddef> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template<typename Type> +[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT { +if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) { +return ptr; +} else { +return to_address(std::forward<Type>(ptr).operator->()); +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) { +lhs = rhs; +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) { +lhs = std::move(rhs); +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); + +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) { +using std::swap; +swap(lhs, rhs); +} +} + +/** + * @brief Checks whether a value is a power of two or not. + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value. + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded"); +std::size_t curr = value - (value != 0u); + +for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) { +curr |= curr >> next; +} + +return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT { +ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); +return value & (mod - 1u); +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Args Types of arguments to use to construct the object. + */ +template<typename Allocator> +struct allocation_deleter: private Allocator { +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Pointer type. */ +using pointer = typename std::allocator_traits<Allocator>::pointer; + +/** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ +allocation_deleter(const allocator_type &alloc) +: Allocator{alloc} {} + +/** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ +void operator()(pointer ptr) { +using alloc_traits = typename std::allocator_traits<Allocator>; +alloc_traits::destroy(*this, to_address(ptr)); +alloc_traits::deallocate(*this, ptr, 1u); +} +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template<typename Type, typename Allocator, typename... Args> +auto allocate_unique(Allocator &allocator, Args &&...args) { +static_assert(!std::is_array_v<Type>, "Array types are not supported"); + +using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>; +using allocator_type = typename alloc_traits::allocator_type; + +allocator_type alloc{allocator}; +auto ptr = alloc_traits::allocate(alloc, 1u); + +ENTT_TRY { +alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...); +} +ENTT_CATCH { +alloc_traits::deallocate(alloc, ptr, 1u); +ENTT_THROW; +} + +return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc}; +} + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type> +struct uses_allocator_construction { +template<typename Allocator, typename... Params> +static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT { +if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) { +return std::forward_as_tuple(std::forward<Params>(params)...); +} else { +static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request"); + +if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) { +return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...); +} else { +static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request"); +return std::forward_as_tuple(std::forward<Params>(params)..., allocator); +} +} +} +}; + +template<typename Type, typename Other> +struct uses_allocator_construction<std::pair<Type, Other>> { +using type = std::pair<Type, Other>; + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT { +return std::make_tuple( +std::piecewise_construct, +std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)), +std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second))); +} + +template<typename Allocator> +static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second))); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT { +return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { +return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { +return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...)); +} + +} // namespace entt + +#endif + +// #include "core/monostate.hpp" +#ifndef ENTT_CORE_MONOSTATE_HPP +#define ENTT_CORE_MONOSTATE_HPP + +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Minimal implementation of the monostate pattern. + * + * A minimal, yet complete configuration system built on top of the monostate + * pattern. Thread safe by design, it works only with basic types like `int`s or + * `bool`s.<br/> + * Multiple types and therefore more than one value can be associated with a + * single key. Because of this, users must pay attention to use the same type + * both during an assignment and when they try to read back their data. + * Otherwise, they can incur in unexpected results. + */ +template<id_type> +struct monostate { +/** + * @brief Assigns a value of a specific type to a given key. + * @tparam Type Type of the value to assign. + * @param val User data to assign to the given key. + */ +template<typename Type> +void operator=(Type val) const ENTT_NOEXCEPT { +value<Type> = val; +} + +/** + * @brief Gets a value of a specific type for a given key. + * @tparam Type Type of the value to get. + * @return Stored value, if any. + */ +template<typename Type> +operator Type() const ENTT_NOEXCEPT { +return value<Type>; +} + +private: +template<typename Type> +inline static ENTT_MAYBE_ATOMIC(Type) value{}; +}; + +/** + * @brief Helper variable template. + * @tparam Value Value used to differentiate between different variables. + */ +template<id_type Value> +inline monostate<Value> monostate_v = {}; + +} // namespace entt + +#endif + +// #include "core/tuple.hpp" +#ifndef ENTT_CORE_TUPLE_HPP +#define ENTT_CORE_TUPLE_HPP + +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Utility function to unwrap tuples of a single element. + * @tparam Type Tuple type of any sizes. + * @param value A tuple object of the given type. + * @return The tuple itself if it contains more than one element, the first + * element otherwise. + */ +template<typename Type> +constexpr decltype(auto) unwrap_tuple(Type &&value) ENTT_NOEXCEPT { +if constexpr(std::tuple_size_v<std::remove_reference_t<Type>> == 1u) { +return std::get<0>(std::forward<Type>(value)); +} else { +return std::forward<Type>(value); +} +} + +} // namespace entt + +#endif + +// #include "core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include <string_view> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +struct ENTT_API type_index final { +[[nodiscard]] static id_type next() ENTT_NOEXCEPT { +static ENTT_MAYBE_ATOMIC(id_type) value{}; +return value++; +} +}; + +template<typename Type> +[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION +std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; +auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); +auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); +return value; +#else +return std::string_view{""}; +#endif +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { +constexpr auto value = stripped_type_name<Type>(); +return value; +} + +template<typename Type> +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { +static const auto value = stripped_type_name<Type>(); +return value; +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { +constexpr auto stripped = stripped_type_name<Type>(); +constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); +return value; +} + +template<typename Type> +[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { +static const auto value = [](const auto stripped) { +return hashed_string::value(stripped.data(), stripped.size()); +}(stripped_type_name<Type>()); +return value; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template<typename Type, typename = void> +struct ENTT_API type_index final { +/** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ +[[nodiscard]] static id_type value() ENTT_NOEXCEPT { +static const id_type value = internal::type_index::next(); +return value; +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template<typename Type, typename = void> +struct type_hash final { +/** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION +[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { +return internal::type_hash<Type>(0); +#else +[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { +return type_index<Type>::value(); +#endif +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template<typename Type, typename = void> +struct type_name final { +/** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ +[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { +return internal::type_name<Type>(0); +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { +return value(); +} +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { +/** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ +template<typename Type> +constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT +: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, +identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, +alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {} + +/** + * @brief Type index. + * @return Type index. + */ +[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT { +return seq; +} + +/** + * @brief Type hash. + * @return Type hash. + */ +[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { +return identifier; +} + +/** + * @brief Type name. + * @return Type name. + */ +[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT { +return alias; +} + +private: +id_type seq; +id_type identifier; +std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.<br/> + * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template<typename Type> +[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT { +if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) { +static type_info instance{std::in_place_type<Type>}; +return instance; +} else { +return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} +} + +/*! @copydoc type_id */ +template<typename Type> +[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT { +return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} + +} // namespace entt + +#endif + +// #include "core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + +// #include "core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { +/*! @brief Indicates that this is a transparent function object. */ +using is_transparent = void; + +/** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ +template<class Type> +[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { +return std::forward<Type>(value); +} +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template<typename Type, typename Class> +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template<typename Func> +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template<class... Func> +struct overloaded: Func... { +using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template<class... Func> +overloaded(Func...) -> overloaded<Func...>; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template<class Func> +struct y_combinator { +/** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ +y_combinator(Func recursive) +: func{std::move(recursive)} {} + +/** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ +template<class... Args> +decltype(auto) operator()(Args &&...args) const { +return func(*this, std::forward<Args>(args)...); +} + +/*! @copydoc operator()() */ +template<class... Args> +decltype(auto) operator()(Args &&...args) { +return func(*this, std::forward<Args>(args)...); +} + +private: +Func func; +}; + +} // namespace entt + +#endif + +// #include "entity/component.hpp" +#ifndef ENTT_ENTITY_COMPONENT_HPP +#define ENTT_ENTITY_COMPONENT_HPP + +#include <cstddef> +#include <type_traits> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct in_place_delete: std::false_type {}; + +template<typename Type> +struct in_place_delete<Type, std::enable_if_t<Type::in_place_delete>> +: std::true_type {}; + +template<typename Type, typename = void> +struct page_size: std::integral_constant<std::size_t, (ENTT_IGNORE_IF_EMPTY && std::is_empty_v<Type>) ? 0u : ENTT_PACKED_PAGE> {}; + +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> {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Common way to access various properties of components. + * @tparam Type Type of component. + */ +template<typename Type, typename = void> +struct component_traits { +static_assert(std::is_same_v<std::decay_t<Type>, Type>, "Unsupported type"); + +/*! @brief Pointer stability, default is `false`. */ +static constexpr bool in_place_delete = internal::in_place_delete<Type>::value; +/*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */ +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 = (component_traits<Type>::page_size == 0u); + +} // namespace entt + +#endif + +// #include "entity/entity.hpp" +#ifndef ENTT_ENTITY_ENTITY_HPP +#define ENTT_ENTITY_ENTITY_HPP + +#include <cstddef> +#include <cstdint> +#include <type_traits> +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_ENTITY_FWD_HPP +#define ENTT_ENTITY_FWD_HPP + +#include <memory> +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include <cstdint> +#include <type_traits> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)> +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "utility.hpp" +#ifndef ENTT_ENTITY_UTILITY_HPP +#define ENTT_ENTITY_UTILITY_HPP + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include <cstdint> +#include <type_traits> +// #include "../config/config.h" + + +namespace entt { + +template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)> +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Alias for exclusion lists. + * @tparam Type List of types. + */ +template<typename... Type> +struct exclude_t: type_list<Type...> {}; + +/** + * @brief Variable template for exclusion lists. + * @tparam Type List of types. + */ +template<typename... Type> +inline constexpr exclude_t<Type...> exclude{}; + +/** + * @brief Alias for lists of observed components. + * @tparam Type List of types. + */ +template<typename... Type> +struct get_t: type_list<Type...> {}; + +/** + * @brief Variable template for lists of observed components. + * @tparam Type List of types. + */ +template<typename... Type> +inline constexpr get_t<Type...> get{}; + +/** + * @brief Alias for lists of owned components. + * @tparam Type List of types. + */ +template<typename... Type> +struct owned_t: type_list<Type...> {}; + +/** + * @brief Variable template for lists of owned components. + * @tparam Type List of types. + */ +template<typename... Type> +inline constexpr owned_t<Type...> owned{}; + +} // namespace entt + +#endif + + +namespace entt { + +template<typename Entity, typename = std::allocator<Entity>> +class basic_sparse_set; + +template<typename, typename Type, typename = std::allocator<Type>, typename = void> +class basic_storage; + +template<typename> +class basic_registry; + +template<typename, typename, typename, typename = void> +class basic_view; + +template<typename> +struct basic_runtime_view; + +template<typename, typename, typename, typename> +class basic_group; + +template<typename> +class basic_observer; + +template<typename> +class basic_organizer; + +template<typename, typename...> +struct basic_handle; + +template<typename> +class basic_snapshot; + +template<typename> +class basic_snapshot_loader; + +template<typename> +class basic_continuous_loader; + +/*! @brief Default entity identifier. */ +enum class entity : id_type {}; + +/*! @brief Alias declaration for the most common use case. */ +using sparse_set = basic_sparse_set<entity>; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Args Other template parameters. + */ +template<typename... Args> +using storage = basic_storage<entity, Args...>; + +/*! @brief Alias declaration for the most common use case. */ +using registry = basic_registry<entity>; + +/*! @brief Alias declaration for the most common use case. */ +using observer = basic_observer<entity>; + +/*! @brief Alias declaration for the most common use case. */ +using organizer = basic_organizer<entity>; + +/*! @brief Alias declaration for the most common use case. */ +using handle = basic_handle<entity>; + +/*! @brief Alias declaration for the most common use case. */ +using const_handle = basic_handle<const entity>; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Args Other template parameters. + */ +template<typename... Args> +using handle_view = basic_handle<entity, Args...>; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Args Other template parameters. + */ +template<typename... Args> +using const_handle_view = basic_handle<const entity, Args...>; + +/*! @brief Alias declaration for the most common use case. */ +using snapshot = basic_snapshot<entity>; + +/*! @brief Alias declaration for the most common use case. */ +using snapshot_loader = basic_snapshot_loader<entity>; + +/*! @brief Alias declaration for the most common use case. */ +using continuous_loader = basic_continuous_loader<entity>; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Get Types of components iterated by the view. + * @tparam Exclude Types of components used to filter the view. + */ +template<typename Get, typename Exclude = exclude_t<>> +using view = basic_view<entity, Get, Exclude>; + +/*! @brief Alias declaration for the most common use case. */ +using runtime_view = basic_runtime_view<sparse_set>; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Args Other template parameters. + */ +template<typename... Args> +using group = basic_group<entity, Args...>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +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>> {}; + +template<typename Type> +struct entt_traits<Type, std::enable_if_t<std::is_class_v<Type>>> +: entt_traits<typename Type::entity_type> {}; + +template<> +struct entt_traits<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 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 + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Entity traits. + * @tparam Type Type of identifier. + */ +template<typename Type> +class entt_traits: internal::entt_traits<Type> { +using base_type = internal::entt_traits<Type>; + +public: +/*! @brief Value type. */ +using value_type = Type; +/*! @brief Underlying entity type. */ +using entity_type = typename base_type::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; + +/** + * @brief Converts an entity to its underlying type. + * @param value The value to convert. + * @return The integral representation of the given value. + */ +[[nodiscard]] static constexpr entity_type to_integral(const value_type value) ENTT_NOEXCEPT { +return static_cast<entity_type>(value); +} + +/** + * @brief Returns the entity part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ +[[nodiscard]] static constexpr entity_type to_entity(const value_type value) ENTT_NOEXCEPT { +return (to_integral(value) & base_type::entity_mask); +} + +/** + * @brief Returns the version part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the version part. + */ +[[nodiscard]] static constexpr version_type to_version(const value_type value) ENTT_NOEXCEPT { +return (to_integral(value) >> base_type::entity_shift); +} + +/** + * @brief Constructs an identifier from its parts. + * + * If the version part is not provided, a tombstone is returned.<br/> + * If the entity part is not provided, a null identifier is returned. + * + * @param entity The entity part of the identifier. + * @param version The version part of the identifier. + * @return A properly constructed identifier. + */ +[[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) ENTT_NOEXCEPT { +return value_type{(entity & base_type::entity_mask) | (static_cast<entity_type>(version) << base_type::entity_shift)}; +} + +/** + * @brief Combines two identifiers in a single one. + * + * The returned identifier is a copy of the first element except for its + * version, which is taken from the second element. + * + * @param lhs The identifier from which to take the entity part. + * @param rhs The identifier from which to take the version part. + * @return A properly constructed identifier. + */ +[[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) ENTT_NOEXCEPT { +constexpr auto mask = (base_type::version_mask << base_type::entity_shift); +return value_type{(lhs & base_type::entity_mask) | (rhs & mask)}; +} +}; + +/** + * @copydoc entt_traits<Entity>::to_integral + * @tparam Entity The value type. + */ +template<typename Entity> +[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_integral(const Entity value) ENTT_NOEXCEPT { +return entt_traits<Entity>::to_integral(value); +} + +/** + * @copydoc entt_traits<Entity>::to_entity + * @tparam Entity The value type. + */ +template<typename Entity> +[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_entity(const Entity value) ENTT_NOEXCEPT { +return entt_traits<Entity>::to_entity(value); +} + +/** + * @copydoc entt_traits<Entity>::to_version + * @tparam Entity The value type. + */ +template<typename Entity> +[[nodiscard]] constexpr typename entt_traits<Entity>::version_type to_version(const Entity value) ENTT_NOEXCEPT { +return entt_traits<Entity>::to_version(value); +} + +/*! @brief Null object for all identifiers. */ +struct null_t { +/** + * @brief Converts the null object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The null representation for the given type. + */ +template<typename Entity> +[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT { +using entity_traits = entt_traits<Entity>; +return entity_traits::combine(entity_traits::reserved, entity_traits::reserved); +} + +/** + * @brief Compares two null objects. + * @param other A null object. + * @return True in all cases. + */ +[[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT { +return true; +} + +/** + * @brief Compares two null objects. + * @param other A null object. + * @return False in all cases. + */ +[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT { +return false; +} + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT { +using entity_traits = entt_traits<Entity>; +return entity_traits::to_entity(entity) == entity_traits::to_entity(*this); +} + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT { +return !(entity == *this); +} +}; + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A null object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) ENTT_NOEXCEPT { +return other.operator==(entity); +} + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A null object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) ENTT_NOEXCEPT { +return !(other == entity); +} + +/*! @brief Tombstone object for all identifiers. */ +struct tombstone_t { +/** + * @brief Converts the tombstone object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The tombstone representation for the given type. + */ +template<typename Entity> +[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT { +using entity_traits = entt_traits<Entity>; +return entity_traits::combine(entity_traits::reserved, entity_traits::reserved); +} + +/** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return True in all cases. + */ +[[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT { +return true; +} + +/** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return False in all cases. + */ +[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT { +return false; +} + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT { +using entity_traits = entt_traits<Entity>; +return entity_traits::to_version(entity) == entity_traits::to_version(*this); +} + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT { +return !(entity == *this); +} +}; + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A tombstone object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT { +return other.operator==(entity); +} + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A tombstone object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT { +return !(other == entity); +} + +/** + * @brief Compile-time constant for null entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the null + * entity and any other identifier. + */ +inline constexpr null_t null{}; + +/** + * @brief Compile-time constant for tombstone entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the + * tombstone entity and any other identifier. + */ +inline constexpr tombstone_t tombstone{}; + +} // namespace entt + +#endif + +// #include "entity/group.hpp" +#ifndef ENTT_ENTITY_GROUP_HPP +#define ENTT_ENTITY_GROUP_HPP + +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include <iterator> +#include <memory> +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template<typename Type> +struct input_iterator_pointer final { +/*! @brief Pointer type. */ +using pointer = Type *; + +/*! @brief Default copy constructor, deleted on purpose. */ +input_iterator_pointer(const input_iterator_pointer &) = delete; + +/*! @brief Default move constructor. */ +input_iterator_pointer(input_iterator_pointer &&) = default; + +/** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ +input_iterator_pointer(Type &&val) +: value{std::move(val)} {} + +/** + * @brief Default copy assignment operator, deleted on purpose. + * @return This proxy object. + */ +input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; + +/** + * @brief Default move assignment operator. + * @return This proxy object. + */ +input_iterator_pointer &operator=(input_iterator_pointer &&) = default; + +/** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ +[[nodiscard]] pointer operator->() ENTT_NOEXCEPT { +return std::addressof(value); +} + +private: +Type value; +}; + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template<typename It, typename Sentinel = It> +struct iterable_adaptor final { +/*! @brief Value type. */ +using value_type = typename std::iterator_traits<It>::value_type; +/*! @brief Iterator type. */ +using iterator = It; +/*! @brief Sentinel type. */ +using sentinel = Sentinel; + +/*! @brief Default constructor. */ +iterable_adaptor() = default; + +/** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ +iterable_adaptor(iterator from, sentinel to) +: first{from}, +last{to} {} + +/** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return first; +} + +/** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ +[[nodiscard]] sentinel end() const ENTT_NOEXCEPT { +return last; +} + +/*! @copydoc begin */ +[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { +return begin(); +} + +/*! @copydoc end */ +[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT { +return end(); +} + +private: +It first; +Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" + +// #include "component.hpp" +#ifndef ENTT_ENTITY_COMPONENT_HPP +#define ENTT_ENTITY_COMPONENT_HPP + +#include <cstddef> +#include <type_traits> +// #include "../config/config.h" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct in_place_delete: std::false_type {}; + +template<typename Type> +struct in_place_delete<Type, std::enable_if_t<Type::in_place_delete>> +: std::true_type {}; + +template<typename Type, typename = void> +struct page_size: std::integral_constant<std::size_t, (ENTT_IGNORE_IF_EMPTY && std::is_empty_v<Type>) ? 0u : ENTT_PACKED_PAGE> {}; + +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> {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Common way to access various properties of components. + * @tparam Type Type of component. + */ +template<typename Type, typename = void> +struct component_traits { +static_assert(std::is_same_v<std::decay_t<Type>, Type>, "Unsupported type"); + +/*! @brief Pointer stability, default is `false`. */ +static constexpr bool in_place_delete = internal::in_place_delete<Type>::value; +/*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */ +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 = (component_traits<Type>::page_size == 0u); + +} // namespace entt + +#endif + +// #include "entity.hpp" +#ifndef ENTT_ENTITY_ENTITY_HPP +#define ENTT_ENTITY_ENTITY_HPP + +#include <cstddef> +#include <cstdint> +#include <type_traits> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +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>> {}; + +template<typename Type> +struct entt_traits<Type, std::enable_if_t<std::is_class_v<Type>>> +: entt_traits<typename Type::entity_type> {}; + +template<> +struct entt_traits<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 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 + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Entity traits. + * @tparam Type Type of identifier. + */ +template<typename Type> +class entt_traits: internal::entt_traits<Type> { +using base_type = internal::entt_traits<Type>; + +public: +/*! @brief Value type. */ +using value_type = Type; +/*! @brief Underlying entity type. */ +using entity_type = typename base_type::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; + +/** + * @brief Converts an entity to its underlying type. + * @param value The value to convert. + * @return The integral representation of the given value. + */ +[[nodiscard]] static constexpr entity_type to_integral(const value_type value) ENTT_NOEXCEPT { +return static_cast<entity_type>(value); +} + +/** + * @brief Returns the entity part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ +[[nodiscard]] static constexpr entity_type to_entity(const value_type value) ENTT_NOEXCEPT { +return (to_integral(value) & base_type::entity_mask); +} + +/** + * @brief Returns the version part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the version part. + */ +[[nodiscard]] static constexpr version_type to_version(const value_type value) ENTT_NOEXCEPT { +return (to_integral(value) >> base_type::entity_shift); +} + +/** + * @brief Constructs an identifier from its parts. + * + * If the version part is not provided, a tombstone is returned.<br/> + * If the entity part is not provided, a null identifier is returned. + * + * @param entity The entity part of the identifier. + * @param version The version part of the identifier. + * @return A properly constructed identifier. + */ +[[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) ENTT_NOEXCEPT { +return value_type{(entity & base_type::entity_mask) | (static_cast<entity_type>(version) << base_type::entity_shift)}; +} + +/** + * @brief Combines two identifiers in a single one. + * + * The returned identifier is a copy of the first element except for its + * version, which is taken from the second element. + * + * @param lhs The identifier from which to take the entity part. + * @param rhs The identifier from which to take the version part. + * @return A properly constructed identifier. + */ +[[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) ENTT_NOEXCEPT { +constexpr auto mask = (base_type::version_mask << base_type::entity_shift); +return value_type{(lhs & base_type::entity_mask) | (rhs & mask)}; +} +}; + +/** + * @copydoc entt_traits<Entity>::to_integral + * @tparam Entity The value type. + */ +template<typename Entity> +[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_integral(const Entity value) ENTT_NOEXCEPT { +return entt_traits<Entity>::to_integral(value); +} + +/** + * @copydoc entt_traits<Entity>::to_entity + * @tparam Entity The value type. + */ +template<typename Entity> +[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_entity(const Entity value) ENTT_NOEXCEPT { +return entt_traits<Entity>::to_entity(value); +} + +/** + * @copydoc entt_traits<Entity>::to_version + * @tparam Entity The value type. + */ +template<typename Entity> +[[nodiscard]] constexpr typename entt_traits<Entity>::version_type to_version(const Entity value) ENTT_NOEXCEPT { +return entt_traits<Entity>::to_version(value); +} + +/*! @brief Null object for all identifiers. */ +struct null_t { +/** + * @brief Converts the null object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The null representation for the given type. + */ +template<typename Entity> +[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT { +using entity_traits = entt_traits<Entity>; +return entity_traits::combine(entity_traits::reserved, entity_traits::reserved); +} + +/** + * @brief Compares two null objects. + * @param other A null object. + * @return True in all cases. + */ +[[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT { +return true; +} + +/** + * @brief Compares two null objects. + * @param other A null object. + * @return False in all cases. + */ +[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT { +return false; +} + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT { +using entity_traits = entt_traits<Entity>; +return entity_traits::to_entity(entity) == entity_traits::to_entity(*this); +} + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT { +return !(entity == *this); +} +}; + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A null object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) ENTT_NOEXCEPT { +return other.operator==(entity); +} + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A null object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) ENTT_NOEXCEPT { +return !(other == entity); +} + +/*! @brief Tombstone object for all identifiers. */ +struct tombstone_t { +/** + * @brief Converts the tombstone object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The tombstone representation for the given type. + */ +template<typename Entity> +[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT { +using entity_traits = entt_traits<Entity>; +return entity_traits::combine(entity_traits::reserved, entity_traits::reserved); +} + +/** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return True in all cases. + */ +[[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT { +return true; +} + +/** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return False in all cases. + */ +[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT { +return false; +} + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT { +using entity_traits = entt_traits<Entity>; +return entity_traits::to_version(entity) == entity_traits::to_version(*this); +} + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT { +return !(entity == *this); +} +}; + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A tombstone object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT { +return other.operator==(entity); +} + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A tombstone object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template<typename Entity> +[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT { +return !(other == entity); +} + +/** + * @brief Compile-time constant for null entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the null + * entity and any other identifier. + */ +inline constexpr null_t null{}; + +/** + * @brief Compile-time constant for tombstone entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the + * tombstone entity and any other identifier. + */ +inline constexpr tombstone_t tombstone{}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" +#ifndef ENTT_ENTITY_SPARSE_SET_HPP +#define ENTT_ENTITY_SPARSE_SET_HPP + +#include <cstddef> +#include <iterator> +#include <memory> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" + +// #include "../core/algorithm.hpp" +#ifndef ENTT_CORE_ALGORITHM_HPP +#define ENTT_CORE_ALGORITHM_HPP + +#include <algorithm> +#include <functional> +#include <iterator> +#include <utility> +#include <vector> +// #include "utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { +/*! @brief Indicates that this is a transparent function object. */ +using is_transparent = void; + +/** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ +template<class Type> +[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { +return std::forward<Type>(value); +} +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template<typename Type, typename Class> +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template<typename Func> +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template<class... Func> +struct overloaded: Func... { +using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template<class... Func> +overloaded(Func...) -> overloaded<Func...>; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template<class Func> +struct y_combinator { +/** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ +y_combinator(Func recursive) +: func{std::move(recursive)} {} + +/** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ +template<class... Args> +decltype(auto) operator()(Args &&...args) const { +return func(*this, std::forward<Args>(args)...); +} + +/*! @copydoc operator()() */ +template<class... Args> +decltype(auto) operator()(Args &&...args) { +return func(*this, std::forward<Args>(args)...); +} + +private: +Func func; +}; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Function object to wrap `std::sort` in a class type. + * + * Unfortunately, `std::sort` cannot be passed as template argument to a class + * template or a function template.<br/> + * This class fills the gap by wrapping some flavors of `std::sort` in a + * function object. + */ +struct std_sort { +/** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @tparam Args Types of arguments to forward to the sort function. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + * @param args Arguments to forward to the sort function, if any. + */ +template<typename It, typename Compare = std::less<>, typename... Args> +void operator()(It first, It last, Compare compare = Compare{}, Args &&...args) const { +std::sort(std::forward<Args>(args)..., std::move(first), std::move(last), std::move(compare)); +} +}; + +/*! @brief Function object for performing insertion sort. */ +struct insertion_sort { +/** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + */ +template<typename It, typename Compare = std::less<>> +void operator()(It first, It last, Compare compare = Compare{}) const { +if(first < last) { +for(auto it = first + 1; it < last; ++it) { +auto value = std::move(*it); +auto pre = it; + +for(; pre > first && compare(value, *(pre - 1)); --pre) { +*pre = std::move(*(pre - 1)); +} + +*pre = std::move(value); +} +} +} +}; + +/** + * @brief Function object for performing LSD radix sort. + * @tparam Bit Number of bits processed per pass. + * @tparam N Maximum number of bits to sort. + */ +template<std::size_t Bit, std::size_t N> +struct radix_sort { +static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass"); + +/** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given _getter_ to access the + * actual data to be sorted. + * + * This implementation is inspired by the online book + * [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort). + * + * @tparam It Type of random access iterator. + * @tparam Getter Type of _getter_ function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param getter A valid _getter_ function object. + */ +template<typename It, typename Getter = identity> +void operator()(It first, It last, Getter getter = Getter{}) const { +if(first < last) { +static constexpr auto mask = (1 << Bit) - 1; +static constexpr auto buckets = 1 << Bit; +static constexpr auto passes = N / Bit; + +using value_type = typename std::iterator_traits<It>::value_type; +std::vector<value_type> aux(std::distance(first, last)); + +auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) { +std::size_t index[buckets]{}; +std::size_t count[buckets]{}; + +for(auto it = from; it != to; ++it) { +++count[(getter(*it) >> start) & mask]; +} + +for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) { +index[pos + 1u] = index[pos] + count[pos]; +} + +for(auto it = from; it != to; ++it) { +out[index[(getter(*it) >> start) & mask]++] = std::move(*it); +} +}; + +for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) { +part(first, last, aux.begin(), pass * Bit); +part(aux.begin(), aux.end(), first, (pass + 1) * Bit); +} + +if constexpr(passes & 1) { +part(first, last, aux.begin(), (passes - 1) * Bit); +std::move(aux.begin(), aux.end(), first); +} +} +} +}; + +} // namespace entt + +#endif + +// #include "../core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + +#include <cstddef> +#include <memory> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { +/*! @brief Indicates that this is a transparent function object. */ +using is_transparent = void; + +/** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ +template<class Type> +[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { +return std::forward<Type>(value); +} +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template<typename Type, typename Class> +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template<typename Func> +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template<class... Func> +struct overloaded: Func... { +using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template<class... Func> +overloaded(Func...) -> overloaded<Func...>; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template<class Func> +struct y_combinator { +/** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ +y_combinator(Func recursive) +: func{std::move(recursive)} {} + +/** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ +template<class... Args> +decltype(auto) operator()(Args &&...args) const { +return func(*this, std::forward<Args>(args)...); +} + +/*! @copydoc operator()() */ +template<class... Args> +decltype(auto) operator()(Args &&...args) { +return func(*this, std::forward<Args>(args)...); +} + +private: +Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include <string_view> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#endif + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include <cstddef> +#include <cstdint> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename> +struct fnv1a_traits; + +template<> +struct fnv1a_traits<std::uint32_t> { +using type = std::uint32_t; +static constexpr std::uint32_t offset = 2166136261; +static constexpr std::uint32_t prime = 16777619; +}; + +template<> +struct fnv1a_traits<std::uint64_t> { +using type = std::uint64_t; +static constexpr std::uint64_t offset = 14695981039346656037ull; +static constexpr std::uint64_t prime = 1099511628211ull; +}; + +template<typename Char> +struct basic_hashed_string { +using value_type = Char; +using size_type = std::size_t; +using hash_type = id_type; + +const value_type *repr; +size_type length; +hash_type hash; +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers in the codebase while using their numeric + * counterparts at runtime.<br/> + * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +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>; + +struct const_wrapper { +// non-explicit constructor on purpose +constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {} +const Char *repr; +}; + +// Fowler–Noll–Vo hash function v. 1a - the good +[[nodiscard]] static constexpr auto helper(const Char *str) ENTT_NOEXCEPT { +base_type base{str, 0u, hs_traits::offset}; + +for(; str[base.length]; ++base.length) { +base.hash = (base.hash ^ static_cast<hs_traits::type>(str[base.length])) * hs_traits::prime; +} + +return base; +} + +// Fowler–Noll–Vo hash function v. 1a - the good +[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) ENTT_NOEXCEPT { +base_type base{str, len, hs_traits::offset}; + +for(size_type pos{}; pos < len; ++pos) { +base.hash = (base.hash ^ static_cast<hs_traits::type>(str[pos])) * hs_traits::prime; +} + +return base; +} + +public: +/*! @brief Character type. */ +using value_type = typename base_type::value_type; +/*! @brief Unsigned integer type. */ +using size_type = typename base_type::size_type; +/*! @brief Unsigned integer type. */ +using hash_type = typename base_type::hash_type; + +/** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ +[[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) ENTT_NOEXCEPT { +return basic_hashed_string{str, len}; +} + +/** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ +template<std::size_t N> +[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { +return basic_hashed_string{str}; +} + +/** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ +[[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { +return basic_hashed_string{wrapper}; +} + +/*! @brief Constructs an empty hashed string. */ +constexpr basic_hashed_string() ENTT_NOEXCEPT +: base_type{} {} + +/** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +constexpr basic_hashed_string(const value_type *str, const size_type len) ENTT_NOEXCEPT +: base_type{helper(str, len)} {} + +/** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template<std::size_t N> +constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT +: base_type{helper(str)} {} + +/** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ +explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT +: base_type{helper(wrapper.repr)} {} + +/** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ +[[nodiscard]] constexpr size_type size() const ENTT_NOEXCEPT { +return base_type::length; +} + +/** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ +[[nodiscard]] constexpr const value_type *data() const ENTT_NOEXCEPT { +return base_type::repr; +} + +/** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ +[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { +return base_type::hash; +} + +/*! @copydoc data */ +[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { +return data(); +} + +/** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ +[[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template<typename Char> +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template<typename Char, std::size_t N> +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>; + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string<char>; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string<wchar_t>; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT { +return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { +return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +struct ENTT_API type_index final { +[[nodiscard]] static id_type next() ENTT_NOEXCEPT { +static ENTT_MAYBE_ATOMIC(id_type) value{}; +return value++; +} +}; + +template<typename Type> +[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION +std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; +auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); +auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); +return value; +#else +return std::string_view{""}; +#endif +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { +constexpr auto value = stripped_type_name<Type>(); +return value; +} + +template<typename Type> +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { +static const auto value = stripped_type_name<Type>(); +return value; +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { +constexpr auto stripped = stripped_type_name<Type>(); +constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); +return value; +} + +template<typename Type> +[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { +static const auto value = [](const auto stripped) { +return hashed_string::value(stripped.data(), stripped.size()); +}(stripped_type_name<Type>()); +return value; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template<typename Type, typename = void> +struct ENTT_API type_index final { +/** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ +[[nodiscard]] static id_type value() ENTT_NOEXCEPT { +static const id_type value = internal::type_index::next(); +return value; +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template<typename Type, typename = void> +struct type_hash final { +/** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION +[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { +return internal::type_hash<Type>(0); +#else +[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { +return type_index<Type>::value(); +#endif +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template<typename Type, typename = void> +struct type_name final { +/** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ +[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { +return internal::type_name<Type>(0); +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { +return value(); +} +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { +/** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ +template<typename Type> +constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT +: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, +identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, +alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {} + +/** + * @brief Type index. + * @return Type index. + */ +[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT { +return seq; +} + +/** + * @brief Type hash. + * @return Type hash. + */ +[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { +return identifier; +} + +/** + * @brief Type name. + * @return Type name. + */ +[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT { +return alias; +} + +private: +id_type seq; +id_type identifier; +std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.<br/> + * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template<typename Type> +[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT { +if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) { +static type_info instance{std::in_place_type<Type>}; +return instance; +} else { +return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} +} + +/*! @copydoc type_id */ +template<typename Type> +[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT { +return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template<std::size_t Len, std::size_t Align> +class basic_any { +enum class operation : std::uint8_t { +copy, +move, +transfer, +assign, +destroy, +compare, +get +}; + +enum class policy : std::uint8_t { +owner, +ref, +cref +}; + +using storage_type = std::aligned_storage_t<Len + !Len, Align>; +using vtable_type = const void *(const operation, const basic_any &, const void *); + +template<typename Type> +static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>; + +template<typename Type> +static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] const void *other) { +static_assert(!std::is_same_v<Type, void> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type"); +const Type *element = nullptr; + +if constexpr(in_situ<Type>) { +element = value.owner() ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance); +} else { +element = static_cast<const Type *>(value.instance); +} + +switch(op) { +case operation::copy: +if constexpr(std::is_copy_constructible_v<Type>) { +static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element); +} +break; +case operation::move: +if constexpr(in_situ<Type>) { +if(value.owner()) { +return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))}; +} +} + +return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr)); +case operation::transfer: +if constexpr(std::is_move_assignable_v<Type>) { +*const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other))); +return other; +} +[[fallthrough]]; +case operation::assign: +if constexpr(std::is_copy_assignable_v<Type>) { +*const_cast<Type *>(element) = *static_cast<const Type *>(other); +return other; +} +break; +case operation::destroy: +if constexpr(in_situ<Type>) { +element->~Type(); +} else if constexpr(std::is_array_v<Type>) { +delete[] element; +} else { +delete element; +} +break; +case operation::compare: +if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) { +return *static_cast<const Type *>(element) == *static_cast<const Type *>(other) ? other : nullptr; +} else { +return (element == other) ? other : nullptr; +} +case operation::get: +return element; +} + +return nullptr; +} + +template<typename Type, typename... Args> +void initialize([[maybe_unused]] Args &&...args) { +if constexpr(!std::is_void_v<Type>) { +info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +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"); +mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref; +instance = (std::addressof(args), ...); +} else if constexpr(in_situ<Type>) { +if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) { +new(&storage) Type{std::forward<Args>(args)...}; +} else { +new(&storage) Type(std::forward<Args>(args)...); +} +} else { +if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) { +instance = new Type{std::forward<Args>(args)...}; +} else { +instance = new Type(std::forward<Args>(args)...); +} +} +} +} + +basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT +: instance{other.data()}, +info{other.info}, +vtable{other.vtable}, +mode{pol} {} + +public: +/*! @brief Size of the internal storage. */ +static constexpr auto length = Len; +/*! @brief Alignment requirement. */ +static constexpr auto alignment = Align; + +/*! @brief Default constructor. */ +constexpr basic_any() ENTT_NOEXCEPT +: instance{}, +info{&type_id<void>()}, +vtable{}, +mode{policy::owner} {} + +/** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ +template<typename Type, typename... Args> +explicit basic_any(std::in_place_type_t<Type>, Args &&...args) +: basic_any{} { +initialize<Type>(std::forward<Args>(args)...); +} + +/** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ +template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>> +basic_any(Type &&value) +: basic_any{} { +initialize<std::decay_t<Type>>(std::forward<Type>(value)); +} + +/** + * @brief Copy constructor. + * @param other The instance to copy from. + */ +basic_any(const basic_any &other) +: basic_any{} { +if(other.vtable) { +other.vtable(operation::copy, other, this); +} +} + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +basic_any(basic_any &&other) ENTT_NOEXCEPT +: instance{}, +info{other.info}, +vtable{other.vtable}, +mode{other.mode} { +if(other.vtable) { +other.vtable(operation::move, other, this); +} +} + +/*! @brief Frees the internal storage, whatever it means. */ +~basic_any() { +if(vtable && owner()) { +vtable(operation::destroy, *this, nullptr); +} +} + +/** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ +basic_any &operator=(const basic_any &other) { +reset(); + +if(other.vtable) { +other.vtable(operation::copy, other, this); +} + +return *this; +} + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This any object. + */ +basic_any &operator=(basic_any &&other) ENTT_NOEXCEPT { +reset(); + +if(other.vtable) { +other.vtable(operation::move, other, this); +info = other.info; +vtable = other.vtable; +mode = other.mode; +} + +return *this; +} + +/** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This any object. + */ +template<typename Type> +std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>, basic_any &> +operator=(Type &&value) { +emplace<std::decay_t<Type>>(std::forward<Type>(value)); +return *this; +} + +/** + * @brief Returns the object type if any, `type_id<void>()` otherwise. + * @return The object type if any, `type_id<void>()` otherwise. + */ +[[nodiscard]] const type_info &type() const ENTT_NOEXCEPT { +return *info; +} + +/** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ +[[nodiscard]] const void *data() const ENTT_NOEXCEPT { +return vtable ? vtable(operation::get, *this, nullptr) : nullptr; +} + +/** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ +[[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT { +return *info == req ? data() : nullptr; +} + +/** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ +[[nodiscard]] void *data() ENTT_NOEXCEPT { +return (!vtable || mode == policy::cref) ? nullptr : const_cast<void *>(vtable(operation::get, *this, nullptr)); +} + +/** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ +[[nodiscard]] void *data(const type_info &req) ENTT_NOEXCEPT { +return *info == req ? data() : nullptr; +} + +/** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ +template<typename Type, typename... Args> +void emplace(Args &&...args) { +reset(); +initialize<Type>(std::forward<Args>(args)...); +} + +/** + * @brief Assigns a value to the contained object without replacing it. + * @param other The value to assign to the contained object. + * @return True in case of success, false otherwise. + */ +bool assign(const any &other) { +if(vtable && mode != policy::cref && *info == *other.info) { +return (vtable(operation::assign, *this, other.data()) != nullptr); +} + +return false; +} + +/*! @copydoc assign */ +bool assign(any &&other) { +if(vtable && mode != policy::cref && *info == *other.info) { +if(auto *val = other.data(); val) { +return (vtable(operation::transfer, *this, val) != nullptr); +} else { +return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr); +} +} + +return false; +} + +/*! @brief Destroys contained object */ +void reset() { +if(vtable && owner()) { +vtable(operation::destroy, *this, nullptr); +} + +info = &type_id<void>(); +vtable = nullptr; +mode = policy::owner; +} + +/** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return vtable != nullptr; +} + +/** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ +bool operator==(const basic_any &other) const ENTT_NOEXCEPT { +if(vtable && *info == *other.info) { +return (vtable(operation::compare, *this, other.data()) != nullptr); +} + +return (!vtable && !other.vtable); +} + +/** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ +[[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT { +return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)}; +} + +/*! @copydoc as_ref */ +[[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT { +return basic_any{*this, policy::cref}; +} + +/** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ +[[nodiscard]] bool owner() const ENTT_NOEXCEPT { +return (mode == policy::owner); +} + +private: +union { +const void *instance; +storage_type storage; +}; +const type_info *info; +vtable_type *vtable; +policy mode; +}; + +/** + * @brief Checks if two wrappers differ in their content. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param lhs A wrapper, either empty or not. + * @param rhs A wrapper, either empty or not. + * @return True if the two wrappers differ in their content, false otherwise. + */ +template<std::size_t Len, std::size_t Align> +[[nodiscard]] inline bool operator!=(const basic_any<Len, Align> &lhs, const basic_any<Len, Align> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template<typename Type, std::size_t Len, std::size_t Align> +Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT { +const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); +ENTT_ASSERT(instance, "Invalid instance"); +return static_cast<Type>(*instance); +} + +/*! @copydoc any_cast */ +template<typename Type, std::size_t Len, std::size_t Align> +Type any_cast(basic_any<Len, Align> &data) ENTT_NOEXCEPT { +// forces const on non-reference types to make them work also with wrappers for const references +auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data); +ENTT_ASSERT(instance, "Invalid instance"); +return static_cast<Type>(*instance); +} + +/*! @copydoc any_cast */ +template<typename Type, std::size_t Len, std::size_t Align> +Type any_cast(basic_any<Len, Align> &&data) ENTT_NOEXCEPT { +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)); +} else { +return any_cast<Type>(data); +} +} else { +auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); +ENTT_ASSERT(instance, "Invalid instance"); +return static_cast<Type>(std::move(*instance)); +} +} + +/*! @copydoc any_cast */ +template<typename Type, std::size_t Len, std::size_t Align> +const Type *any_cast(const basic_any<Len, Align> *data) ENTT_NOEXCEPT { +const auto &info = type_id<std::remove_cv_t<std::remove_reference_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) ENTT_NOEXCEPT { +const auto &info = type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +// last attempt to make wrappers for const references return their values +return static_cast<Type *>(static_cast<constness_as_t<basic_any<Len, Align>, Type> *>(data)->data(info)); +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @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) { +return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @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) { +return basic_any<Len, Align>{std::in_place_type<std::conditional_t<std::is_rvalue_reference_v<Type>, std::decay_t<Type>, Type>>, std::forward<Type>(value)}; +} + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include <cstddef> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template<typename Type> +[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT { +if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) { +return ptr; +} else { +return to_address(std::forward<Type>(ptr).operator->()); +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) { +lhs = rhs; +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) { +lhs = std::move(rhs); +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); + +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) { +using std::swap; +swap(lhs, rhs); +} +} + +/** + * @brief Checks whether a value is a power of two or not. + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value. + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded"); +std::size_t curr = value - (value != 0u); + +for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) { +curr |= curr >> next; +} + +return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT { +ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); +return value & (mod - 1u); +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Args Types of arguments to use to construct the object. + */ +template<typename Allocator> +struct allocation_deleter: private Allocator { +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Pointer type. */ +using pointer = typename std::allocator_traits<Allocator>::pointer; + +/** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ +allocation_deleter(const allocator_type &alloc) +: Allocator{alloc} {} + +/** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ +void operator()(pointer ptr) { +using alloc_traits = typename std::allocator_traits<Allocator>; +alloc_traits::destroy(*this, to_address(ptr)); +alloc_traits::deallocate(*this, ptr, 1u); +} +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template<typename Type, typename Allocator, typename... Args> +auto allocate_unique(Allocator &allocator, Args &&...args) { +static_assert(!std::is_array_v<Type>, "Array types are not supported"); + +using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>; +using allocator_type = typename alloc_traits::allocator_type; + +allocator_type alloc{allocator}; +auto ptr = alloc_traits::allocate(alloc, 1u); + +ENTT_TRY { +alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...); +} +ENTT_CATCH { +alloc_traits::deallocate(alloc, ptr, 1u); +ENTT_THROW; +} + +return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc}; +} + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type> +struct uses_allocator_construction { +template<typename Allocator, typename... Params> +static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT { +if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) { +return std::forward_as_tuple(std::forward<Params>(params)...); +} else { +static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request"); + +if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) { +return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...); +} else { +static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request"); +return std::forward_as_tuple(std::forward<Params>(params)..., allocator); +} +} +} +}; + +template<typename Type, typename Other> +struct uses_allocator_construction<std::pair<Type, Other>> { +using type = std::pair<Type, Other>; + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT { +return std::make_tuple( +std::piecewise_construct, +std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)), +std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second))); +} + +template<typename Allocator> +static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second))); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT { +return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { +return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { +return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include <string_view> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +struct ENTT_API type_index final { +[[nodiscard]] static id_type next() ENTT_NOEXCEPT { +static ENTT_MAYBE_ATOMIC(id_type) value{}; +return value++; +} +}; + +template<typename Type> +[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION +std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; +auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); +auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); +return value; +#else +return std::string_view{""}; +#endif +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { +constexpr auto value = stripped_type_name<Type>(); +return value; +} + +template<typename Type> +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { +static const auto value = stripped_type_name<Type>(); +return value; +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { +constexpr auto stripped = stripped_type_name<Type>(); +constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); +return value; +} + +template<typename Type> +[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { +static const auto value = [](const auto stripped) { +return hashed_string::value(stripped.data(), stripped.size()); +}(stripped_type_name<Type>()); +return value; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template<typename Type, typename = void> +struct ENTT_API type_index final { +/** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ +[[nodiscard]] static id_type value() ENTT_NOEXCEPT { +static const id_type value = internal::type_index::next(); +return value; +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template<typename Type, typename = void> +struct type_hash final { +/** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION +[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { +return internal::type_hash<Type>(0); +#else +[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { +return type_index<Type>::value(); +#endif +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template<typename Type, typename = void> +struct type_name final { +/** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ +[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { +return internal::type_name<Type>(0); +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { +return value(); +} +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { +/** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ +template<typename Type> +constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT +: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, +identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, +alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {} + +/** + * @brief Type index. + * @return Type index. + */ +[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT { +return seq; +} + +/** + * @brief Type hash. + * @return Type hash. + */ +[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { +return identifier; +} + +/** + * @brief Type name. + * @return Type name. + */ +[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT { +return alias; +} + +private: +id_type seq; +id_type identifier; +std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.<br/> + * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template<typename Type> +[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT { +if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) { +static type_info instance{std::in_place_type<Type>}; +return instance; +} else { +return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} +} + +/*! @copydoc type_id */ +template<typename Type> +[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT { +return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} + +} // namespace entt + +#endif + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Container> +struct sparse_set_iterator final { +using value_type = typename Container::value_type; +using pointer = typename Container::const_pointer; +using reference = typename Container::const_reference; +using difference_type = typename Container::difference_type; +using iterator_category = std::random_access_iterator_tag; + +sparse_set_iterator() ENTT_NOEXCEPT +: packed{}, +offset{} {} + +sparse_set_iterator(const Container &ref, const difference_type idx) ENTT_NOEXCEPT +: packed{std::addressof(ref)}, +offset{idx} {} + +sparse_set_iterator &operator++() ENTT_NOEXCEPT { +return --offset, *this; +} + +sparse_set_iterator operator++(int) ENTT_NOEXCEPT { +sparse_set_iterator orig = *this; +return ++(*this), orig; +} + +sparse_set_iterator &operator--() ENTT_NOEXCEPT { +return ++offset, *this; +} + +sparse_set_iterator operator--(int) ENTT_NOEXCEPT { +sparse_set_iterator orig = *this; +return operator--(), orig; +} + +sparse_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { +offset -= value; +return *this; +} + +sparse_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { +sparse_set_iterator copy = *this; +return (copy += value); +} + +sparse_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { +return (*this += -value); +} + +sparse_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { +return (*this + -value); +} + +[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { +return packed->data()[index() - value]; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return packed->data() + index(); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return *operator->(); +} + +[[nodiscard]] difference_type index() const ENTT_NOEXCEPT { +return offset - 1; +} + +private: +const Container *packed; +difference_type offset; +}; + +template<typename Type, typename Other> +[[nodiscard]] std::ptrdiff_t operator-(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT { +return rhs.index() - lhs.index(); +} + +template<typename Type, typename Other> +[[nodiscard]] bool operator==(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT { +return lhs.index() == rhs.index(); +} + +template<typename Type, typename Other> +[[nodiscard]] bool operator!=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +template<typename Type, typename Other> +[[nodiscard]] bool operator<(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT { +return lhs.index() > rhs.index(); +} + +template<typename Type, typename Other> +[[nodiscard]] bool operator>(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT { +return lhs.index() < rhs.index(); +} + +template<typename Type, typename Other> +[[nodiscard]] bool operator<=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT { +return !(lhs > rhs); +} + +template<typename Type, typename Other> +[[nodiscard]] bool operator>=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @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. + * + * Sparse set or packed array or whatever is the name users give it.<br/> + * 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. + * + * @note + * Internal data structures arrange elements to maximize performance. There are + * no guarantees that entities are returned in the insertion order when iterate + * a sparse set. Do not make assumption on the order in any case. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Entity, typename Allocator> +class basic_sparse_set { +using alloc_traits = std::allocator_traits<Allocator>; +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; +} + +[[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)]; +} + +[[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; + +if(!(page < sparse.size())) { +sparse.resize(page + 1u, nullptr); +} + +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); +} + +auto &elem = sparse[page][fast_mod(pos, entity_traits::page_size)]; +ENTT_ASSERT(entity_traits::to_version(elem) == entity_traits::to_version(tombstone), "Slot not available"); +return elem; +} + +void release_sparse_pages() { +auto page_allocator{packed.get_allocator()}; + +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); +page = nullptr; +} +} +} + +private: +virtual const void *get_at(const std::size_t) const { +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) {} + +protected: +/*! @brief Random access iterator type. */ +using basic_iterator = internal::sparse_set_iterator<packed_container_type>; + +/** + * @brief Erases entities from a sparse set. + * @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. + */ +virtual void swap_and_pop(basic_iterator first, basic_iterator last) { +for(; first != last; ++first) { +sparse_ref(packed.back()) = entity_traits::combine(static_cast<typename entity_traits::entity_type>(first.index()), entity_traits::to_integral(packed.back())); +const auto entt = std::exchange(packed[first.index()], packed.back()); +// unnecessary but it helps to detect nasty bugs +ENTT_ASSERT((packed.back() = tombstone, true), ""); +// lazy self-assignment guard +sparse_ref(entt) = null; +packed.pop_back(); +} +} + +/** + * @brief Erases entities from a sparse set. + * @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. + */ +virtual void in_place_pop(basic_iterator first, basic_iterator last) { +for(; first != last; ++first) { +sparse_ref(*first) = null; +packed[first.index()] = std::exchange(free_list, entity_traits::combine(static_cast<typename entity_traits::entity_type>(first.index()), entity_traits::reserved)); +} +} + +/** + * @brief Assigns an entity to a sparse set. + * @param entt A valid identifier. + * @param force_back Force back insertion. + * @return Iterator pointing to the emplaced element. + */ +virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) { +ENTT_ASSERT(!contains(entt), "Set already contains entity"); + +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)); +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)); +free_list = std::exchange(packed[pos], entt); +return --(end() - pos); +} +} + +public: +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Underlying version type. */ +using version_type = typename entity_traits::version_type; +/*! @brief Unsigned integer type. */ +using size_type = typename packed_container_type::size_type; +/*! @brief Pointer type to contained entities. */ +using pointer = typename packed_container_type::const_pointer; +/*! @brief Random access iterator type. */ +using iterator = basic_iterator; +/*! @brief Constant random access iterator type. */ +using const_iterator = iterator; +/*! @brief Reverse iterator type. */ +using reverse_iterator = std::reverse_iterator<iterator>; +/*! @brief Constant reverse iterator type. */ +using const_reverse_iterator = reverse_iterator; + +/*! @brief Default constructor. */ +basic_sparse_set() +: basic_sparse_set{type_id<void>()} {} + +/** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ +explicit basic_sparse_set(const allocator_type &allocator) +: basic_sparse_set{type_id<void>(), deletion_policy::swap_and_pop, allocator} {} + +/** + * @brief Constructs an empty container with the given policy and allocator. + * @param pol Type of deletion policy. + * @param allocator The allocator to use (possibly default-constructed). + */ +explicit basic_sparse_set(deletion_policy pol, const allocator_type &allocator = {}) +: basic_sparse_set{type_id<void>(), pol, allocator} {} + +/** + * @brief Constructs an empty container with the given value type, policy + * and allocator. + * @param value 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 = {}) +: sparse{allocator}, +packed{allocator}, +info{&value}, +free_list{tombstone}, +mode{pol} {} + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +basic_sparse_set(basic_sparse_set &&other) ENTT_NOEXCEPT +: sparse{std::move(other.sparse)}, +packed{std::move(other.packed)}, +info{other.info}, +free_list{std::exchange(other.free_list, tombstone)}, +mode{other.mode} {} + +/** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ +basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) ENTT_NOEXCEPT +: sparse{std::move(other.sparse), allocator}, +packed{std::move(other.packed), allocator}, +info{other.info}, +free_list{std::exchange(other.free_list, tombstone)}, +mode{other.mode} { +ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed"); +} + +/*! @brief Default destructor. */ +virtual ~basic_sparse_set() { +release_sparse_pages(); +} + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This sparse set. + */ +basic_sparse_set &operator=(basic_sparse_set &&other) ENTT_NOEXCEPT { +ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed"); + +release_sparse_pages(); +sparse = std::move(other.sparse); +packed = std::move(other.packed); +info = other.info; +free_list = std::exchange(other.free_list, tombstone); +mode = other.mode; +return *this; +} + +/** + * @brief Exchanges the contents with those of a given sparse set. + * @param other Sparse set to exchange the content with. + */ +void swap(basic_sparse_set &other) { +using std::swap; +swap(sparse, other.sparse); +swap(packed, other.packed); +swap(info, other.info); +swap(free_list, other.free_list); +swap(mode, other.mode); +} + +/** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ +[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { +return packed.get_allocator(); +} + +/** + * @brief Returns the deletion policy of a sparse set. + * @return The deletion policy of the sparse set. + */ +[[nodiscard]] deletion_policy policy() const ENTT_NOEXCEPT { +return mode; +} + +/** + * @brief Increases the capacity of a sparse set. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ +virtual void reserve(const size_type cap) { +packed.reserve(cap); +} + +/** + * @brief Returns the number of elements that a sparse set has currently + * allocated space for. + * @return Capacity of the sparse set. + */ +[[nodiscard]] virtual size_type capacity() const ENTT_NOEXCEPT { +return packed.capacity(); +} + +/*! @brief Requests the removal of unused capacity. */ +virtual void shrink_to_fit() { +packed.shrink_to_fit(); +} + +/** + * @brief Returns the extent of a sparse set. + * + * The extent of a sparse set is also the size of the internal sparse array. + * There is no guarantee that the internal packed array has the same size. + * Usually the size of the internal sparse array is equal or greater than + * the one of the internal packed array. + * + * @return Extent of the sparse set. + */ +[[nodiscard]] size_type extent() const ENTT_NOEXCEPT { +return sparse.size() * entity_traits::page_size; +} + +/** + * @brief Returns the number of elements in a sparse set. + * + * The number of elements is also the size of the internal packed array. + * There is no guarantee that the internal sparse array has the same size. + * Usually the size of the internal sparse array is equal or greater than + * the one of the internal packed array. + * + * @return Number of elements. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return packed.size(); +} + +/** + * @brief Checks whether a sparse set is empty. + * @return True if the sparse set is empty, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return packed.empty(); +} + +/** + * @brief Direct access to the internal packed array. + * @return A pointer to the internal packed array. + */ +[[nodiscard]] pointer data() const ENTT_NOEXCEPT { +return packed.data(); +} + +/** + * @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 + * `end()`. + * + * @return An iterator to the first entity of the sparse set. + */ +[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { +const auto pos = static_cast<typename iterator::difference_type>(packed.size()); +return iterator{packed, pos}; +} + +/*! @copydoc begin */ +[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { +return begin(); +} + +/** + * @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. + */ +[[nodiscard]] iterator end() const ENTT_NOEXCEPT { +return iterator{packed, {}}; +} + +/*! @copydoc end */ +[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { +return end(); +} + +/** + * @brief Returns a reverse iterator to the beginning. + * + * The returned iterator points to the first entity of the reversed internal + * packed array. If the sparse set is empty, the returned iterator will be + * equal to `rend()`. + * + * @return An iterator to the first entity of the reversed internal packed + * array. + */ +[[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT { +return std::make_reverse_iterator(end()); +} + +/*! @copydoc rbegin */ +[[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT { +return rbegin(); +} + +/** + * @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. + */ +[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { +return std::make_reverse_iterator(begin()); +} + +/*! @copydoc rend */ +[[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT { +return rend(); +} + +/** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ +[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT { +return contains(entt) ? --(end() - index(entt)) : end(); +} + +/** + * @brief Checks if a sparse set contains an entity. + * @param entt A valid identifier. + * @return True if the sparse set contains the entity, false otherwise. + */ +[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { +const auto elem = sparse_ptr(entt); +constexpr auto cap = entity_traits::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); +} + +/** + * @brief Returns the contained version for an identifier. + * @param entt A valid identifier. + * @return The version for the given identifier if present, the tombstone + * version otherwise. + */ +[[nodiscard]] version_type current(const entity_type entt) const ENTT_NOEXCEPT { +const auto elem = sparse_ptr(entt); +constexpr auto fallback = entity_traits::to_version(tombstone); +return elem ? entity_traits::to_version(*elem) : fallback; +} + +/** + * @brief Returns the position of an entity in a sparse set. + * + * @warning + * Attempting to get the position of an entity that doesn't belong to the + * sparse set results in undefined behavior. + * + * @param entt A valid identifier. + * @return The position of the entity in the sparse set. + */ +[[nodiscard]] size_type index(const entity_type entt) const ENTT_NOEXCEPT { +ENTT_ASSERT(contains(entt), "Set does not contain entity"); +return static_cast<size_type>(entity_traits::to_entity(sparse_ref(entt))); +} + +/** + * @brief Returns the entity at specified location, with bounds checking. + * @param pos The position for which to return the entity. + * @return The entity at specified location if any, a null entity otherwise. + */ +[[nodiscard]] entity_type at(const size_type pos) const ENTT_NOEXCEPT { +return pos < packed.size() ? packed[pos] : null; +} + +/** + * @brief Returns the entity at specified location, without bounds checking. + * @param pos The position for which to return the entity. + * @return The entity at specified location. + */ +[[nodiscard]] entity_type operator[](const size_type pos) const ENTT_NOEXCEPT { +ENTT_ASSERT(pos < packed.size(), "Position is out of bounds"); +return packed[pos]; +} + +/** + * @brief Returns the element assigned to an entity, if any. + * + * @warning + * Attempting to use an entity that doesn't belong to the sparse set results + * in undefined behavior. + * + * @param entt A valid identifier. + * @return An opaque pointer to the element assigned to the entity, if any. + */ +const void *get(const entity_type entt) const ENTT_NOEXCEPT { +return get_at(index(entt)); +} + +/*! @copydoc get */ +void *get(const entity_type entt) ENTT_NOEXCEPT { +return const_cast<void *>(std::as_const(*this).get(entt)); +} + +/** + * @brief Assigns an entity to a sparse set. + * + * @warning + * Attempting to assign an entity that already belongs to the sparse set + * results in undefined behavior. + * + * @param entt A valid identifier. + * @param value Optional opaque value 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); +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; +} + +/** + * @brief Assigns one or more entities to a sparse set. + * + * @warning + * Attempting to assign an entity that already belongs to the sparse set + * results in undefined behavior. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return Iterator pointing to the first element inserted in case of + * success, the `end()` iterator otherwise. + */ +template<typename It> +iterator insert(It first, It last) { +for(auto it = first; it != last; ++it) { +try_emplace(*it, true); +} + +return first == last ? end() : find(*first); +} + +/** + * @brief Erases an entity from a sparse set. + * + * @warning + * Attempting to erase an entity that doesn't belong to the sparse set + * results in undefined behavior. + * + * @param entt A valid identifier. + */ +void erase(const entity_type entt) { +const auto it = --(end() - index(entt)); +(mode == deletion_policy::in_place) ? in_place_pop(it, it + 1u) : swap_and_pop(it, it + 1u); +} + +/** + * @brief Erases entities from a set. + * + * @sa erase + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ +template<typename It> +void erase(It first, It last) { +if constexpr(std::is_same_v<It, basic_iterator>) { +(mode == deletion_policy::in_place) ? in_place_pop(first, last) : swap_and_pop(first, last); +} else { +for(; first != last; ++first) { +erase(*first); +} +} +} + +/** + * @brief Removes an entity from a sparse set if it exists. + * @param entt A valid identifier. + * @return True if the entity is actually removed, false otherwise. + */ +bool remove(const entity_type entt) { +return contains(entt) && (erase(entt), true); +} + +/** + * @brief Removes entities from a sparse set if they exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return The number of entities actually removed. + */ +template<typename It> +size_type remove(It first, It last) { +size_type count{}; + +for(; first != last; ++first) { +count += remove(*first); +} + +return count; +} + +/*! @brief Removes all tombstones from the packed array of 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) { +--from; +move_element(from, to); + +using std::swap; +swap(packed[from], 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); +for(; from && packed[from - 1u] == tombstone; --from) {} +} +} + +free_list = tombstone; +packed.resize(from); +} + +/** + * @brief Swaps two entities in a sparse set. + * + * For what it's worth, this function affects both the internal sparse array + * and the internal packed array. Users should not care of that anyway. + * + * @warning + * Attempting to swap entities that don't belong to the sparse set results + * in undefined behavior. + * + * @param lhs A valid identifier. + * @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"); + +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]); +} + +/** + * @brief Sort the first count elements according to the given comparison + * function. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * @endcode + * + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function object must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam 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 length Number of elements to sort. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ +template<typename Compare, typename Sort = std_sort, typename... Args> +void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) { +ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements"); +ENTT_ASSERT(free_list == null, "Partial sorting with tombstones is not supported"); + +algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward<Args>(args)...); + +for(size_type pos{}; pos < length; ++pos) { +auto curr = pos; +auto next = index(packed[curr]); + +while(curr != next) { +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])); +curr = std::exchange(next, idx); +} +} +} + +/** + * @brief Sort all elements according to the given comparison function. + * + * @sa sort_n + * + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ +template<typename Compare, typename Sort = std_sort, typename... Args> +void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { +compact(); +sort_n(packed.size(), std::move(compare), std::move(algo), std::forward<Args>(args)...); +} + +/** + * @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. + * + * @param other The sparse sets that imposes the order of the entities. + */ +void respect(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]) { +// basic no-leak guarantee (with invalid state) if swapping throws +swap_elements(packed[pos], *from); +} + +--pos; +} +} +} + +/*! @brief Clears a sparse set. */ +void clear() { +if(const auto last = end(); free_list == null) { +in_place_pop(begin(), last); +} else { +for(auto &&entity: *this) { +// tombstone filter on itself +if(const auto it = find(entity); it != last) { +in_place_pop(it, it + 1u); +} +} +} + +free_list = tombstone; +packed.clear(); +} + +/** + * @brief Returned value type, if any. + * @return Returned value type, if any. + */ +const type_info &type() const ENTT_NOEXCEPT { +return *info; +} + +/*! @brief Forwards variables to mixins, if any. */ +virtual void bind(any) ENTT_NOEXCEPT {} + +private: +sparse_container_type sparse; +packed_container_type packed; +const type_info *info; +entity_type free_list; +deletion_policy mode; +}; + +} // namespace entt + +#endif + +// #include "storage.hpp" +#ifndef ENTT_ENTITY_STORAGE_HPP +#define ENTT_ENTITY_STORAGE_HPP + +#include <cstddef> +#include <iterator> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include <cstddef> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "type_traits.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type, std::size_t, typename = void> +struct compressed_pair_element { +using reference = Type &; +using const_reference = const Type &; + +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>> +compressed_pair_element() +: value{} {} + +template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>> +compressed_pair_element(Args &&args) +: value{std::forward<Args>(args)} {} + +template<typename... Args, std::size_t... Index> +compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) +: value{std::forward<Args>(std::get<Index>(args))...} {} + +[[nodiscard]] reference get() ENTT_NOEXCEPT { +return value; +} + +[[nodiscard]] const_reference get() const ENTT_NOEXCEPT { +return value; +} + +private: +Type value; +}; + +template<typename Type, std::size_t Tag> +struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type { +using reference = Type &; +using const_reference = const Type &; +using base_type = Type; + +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>> +compressed_pair_element() +: base_type{} {} + +template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>> +compressed_pair_element(Args &&args) +: base_type{std::forward<Args>(args)} {} + +template<typename... Args, std::size_t... Index> +compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) +: base_type{std::forward<Args>(std::get<Index>(args))...} {} + +[[nodiscard]] reference get() ENTT_NOEXCEPT { +return *this; +} + +[[nodiscard]] const_reference get() const ENTT_NOEXCEPT { +return *this; +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +class compressed_pair final +: internal::compressed_pair_element<First, 0u>, +internal::compressed_pair_element<Second, 1u> { +using first_base = internal::compressed_pair_element<First, 0u>; +using second_base = internal::compressed_pair_element<Second, 1u>; + +public: +/*! @brief The type of the first element that the pair stores. */ +using first_type = First; +/*! @brief The type of the second element that the pair stores. */ +using second_type = Second; + +/** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>> +constexpr compressed_pair() +: first_base{}, +second_base{} {} + +/** + * @brief Copy constructor. + * @param other The instance to copy from. + */ +constexpr compressed_pair(const compressed_pair &other) = default; + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +constexpr compressed_pair(compressed_pair &&other) = default; + +/** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ +template<typename Arg, typename Other> +constexpr compressed_pair(Arg &&arg, Other &&other) +: first_base{std::forward<Arg>(arg)}, +second_base{std::forward<Other>(other)} {} + +/** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ +template<typename... Args, typename... Other> +constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) +: first_base{std::move(args), std::index_sequence_for<Args...>{}}, +second_base{std::move(other), std::index_sequence_for<Other...>{}} {} + +/** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ +constexpr compressed_pair &operator=(const compressed_pair &other) = default; + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ +constexpr compressed_pair &operator=(compressed_pair &&other) = default; + +/** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ +[[nodiscard]] first_type &first() ENTT_NOEXCEPT { +return static_cast<first_base &>(*this).get(); +} + +/*! @copydoc first */ +[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { +return static_cast<const first_base &>(*this).get(); +} + +/** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ +[[nodiscard]] second_type &second() ENTT_NOEXCEPT { +return static_cast<second_base &>(*this).get(); +} + +/*! @copydoc second */ +[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { +return static_cast<const second_base &>(*this).get(); +} + +/** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ +void swap(compressed_pair &other) { +using std::swap; +swap(first(), other.first()); +swap(second(), other.second()); +} + +/** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ +template<std::size_t Index> +decltype(auto) get() ENTT_NOEXCEPT { +if constexpr(Index == 0u) { +return first(); +} else { +static_assert(Index == 1u, "Index out of bounds"); +return second(); +} +} + +/*! @copydoc get */ +template<std::size_t Index> +decltype(auto) get() const ENTT_NOEXCEPT { +if constexpr(Index == 0u) { +return first(); +} else { +static_assert(Index == 1u, "Index out of bounds"); +return second(); +} +} +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template<typename Type, typename Other> +compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template<typename First, typename Second> +inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) { +lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<size_t Index, typename First, typename Second> +struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> { +static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_info.hpp" + +// #include "component.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sigh_storage_mixin.hpp" +#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" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + +#include <algorithm> +#include <functional> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + +#include <cstddef> +#include <functional> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include <cstdint> +#include <type_traits> +// #include "../config/config.h" + + +namespace entt { + +template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)> +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_SIGNAL_FWD_HPP +#define ENTT_SIGNAL_FWD_HPP + +#include <memory> + +namespace entt { + +template<typename> +class delegate; + +template<typename = std::allocator<char>> +class basic_dispatcher; + +template<typename> +class emitter; + +class connection; + +struct scoped_connection; + +template<typename> +class sink; + +template<typename Type, typename = std::allocator<Type *>> +class sigh; + +/*! @brief Alias declaration for the most common use case. */ +using dispatcher = basic_dispatcher<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Ret, typename... Args> +auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template<typename Ret, typename Type, typename... Args, typename Other> +auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template<typename Class, typename Ret, typename... Args, typename... Other> +auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template<typename Class, typename Ret, typename... Args, typename... Other> +auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template<typename Class, typename Type, typename... Other> +auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template<typename... Type> +using function_pointer_t = decltype(internal::function_pointer(std::declval<Type>()...)); + +template<typename... Class, typename Ret, typename... Args> +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { +return std::index_sequence_for<Class..., Args...>{}; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @brief Used to wrap a function or a member of a specified type. */ +template<auto> +struct connect_arg_t {}; + +/*! @brief Constant of type connect_arg_t used to disambiguate calls. */ +template<auto Func> +inline constexpr connect_arg_t<Func> connect_arg{}; + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template<typename> +class delegate; + +/** + * @brief Utility class to use to send around functions and members. + * + * Unmanaged delegate for function pointers and members. Users of this class are + * in charge of disconnecting instances before deleting them. + * + * A delegate can be used as a general purpose invoker without memory overhead + * for free functions possibly with payloads and bound or unbound members. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template<typename Ret, typename... Args> +class delegate<Ret(Args...)> { +template<auto Candidate, std::size_t... Index> +[[nodiscard]] auto wrap(std::index_sequence<Index...>) ENTT_NOEXCEPT { +return [](const void *, Args... args) -> Ret { +[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...); +return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...)); +}; +} + +template<auto Candidate, typename Type, std::size_t... Index> +[[nodiscard]] auto wrap(Type &, std::index_sequence<Index...>) ENTT_NOEXCEPT { +return [](const void *payload, Args... args) -> Ret { +[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...); +Type *curr = static_cast<Type *>(const_cast<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))...)); +}; +} + +template<auto Candidate, typename Type, std::size_t... Index> +[[nodiscard]] auto wrap(Type *, std::index_sequence<Index...>) ENTT_NOEXCEPT { +return [](const void *payload, Args... args) -> Ret { +[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...); +Type *curr = static_cast<Type *>(const_cast<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))...)); +}; +} + +public: +/*! @brief Function type of the contained target. */ +using function_type = Ret(const void *, Args...); +/*! @brief Function type of the delegate. */ +using type = Ret(Args...); +/*! @brief Return type of the delegate. */ +using result_type = Ret; + +/*! @brief Default constructor. */ +delegate() ENTT_NOEXCEPT +: instance{nullptr}, +fn{nullptr} {} + +/** + * @brief Constructs a delegate and connects a free function or an unbound + * member. + * @tparam Candidate Function or member to connect to the delegate. + */ +template<auto Candidate> +delegate(connect_arg_t<Candidate>) ENTT_NOEXCEPT { +connect<Candidate>(); +} + +/** + * @brief Constructs a delegate and connects a free function with payload or + * a bound member. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ +template<auto Candidate, typename Type> +delegate(connect_arg_t<Candidate>, Type &&value_or_instance) ENTT_NOEXCEPT { +connect<Candidate>(std::forward<Type>(value_or_instance)); +} + +/** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ +delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { +connect(function, payload); +} + +/** + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. + */ +template<auto Candidate> +void connect() ENTT_NOEXCEPT { +instance = nullptr; + +if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) { +fn = [](const void *, Args... args) -> Ret { +return Ret(std::invoke(Candidate, std::forward<Args>(args)...)); +}; +} else if constexpr(std::is_member_pointer_v<decltype(Candidate)>) { +fn = wrap<Candidate>(internal::index_sequence_for<type_list_element_t<0, type_list<Args...>>>(internal::function_pointer_t<decltype(Candidate)>{})); +} else { +fn = wrap<Candidate>(internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate)>{})); +} +} + +/** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the delegate.<br/> + * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the delegate itself. + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid reference that fits the purpose. + */ +template<auto Candidate, typename Type> +void connect(Type &value_or_instance) ENTT_NOEXCEPT { +instance = &value_or_instance; + +if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, Args...>) { +fn = [](const void *payload, Args... args) -> Ret { +Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload)); +return Ret(std::invoke(Candidate, *curr, std::forward<Args>(args)...)); +}; +} else { +fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{})); +} +} + +/** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * @sa connect(Type &) + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + */ +template<auto Candidate, typename Type> +void connect(Type *value_or_instance) ENTT_NOEXCEPT { +instance = value_or_instance; + +if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, Args...>) { +fn = [](const void *payload, Args... args) -> Ret { +Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload)); +return Ret(std::invoke(Candidate, curr, std::forward<Args>(args)...)); +}; +} else { +fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{})); +} +} + +/** + * @brief Connects an user defined function with optional payload to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of an instance overcomes + * the one of the delegate.<br/> + * The payload is returned as the first argument to the target function in + * all cases. + * + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ +void connect(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { +instance = payload; +fn = function; +} + +/** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ +void reset() ENTT_NOEXCEPT { +instance = nullptr; +fn = nullptr; +} + +/** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ +[[nodiscard]] const void *data() const ENTT_NOEXCEPT { +return instance; +} + +/** + * @brief Triggers a delegate. + * + * The delegate invokes the underlying function and returns the result. + * + * @warning + * Attempting to trigger an invalid delegate results in undefined + * behavior. + * + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. + */ +Ret operator()(Args... args) const { +ENTT_ASSERT(static_cast<bool>(*this), "Uninitialized delegate"); +return fn(instance, std::forward<Args>(args)...); +} + +/** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +// no need to also test instance +return !(fn == nullptr); +} + +/** + * @brief Compares the contents of two delegates. + * @param other Delegate with which to compare. + * @return False if the two contents differ, true otherwise. + */ +[[nodiscard]] bool operator==(const delegate<Ret(Args...)> &other) const ENTT_NOEXCEPT { +return fn == other.fn && instance == other.instance; +} + +private: +const void *instance; +function_type *fn; +}; + +/** + * @brief Compares the contents of two delegates. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two contents differ, false otherwise. + */ +template<typename Ret, typename... Args> +[[nodiscard]] bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template<auto Candidate> +delegate(connect_arg_t<Candidate>) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate)>>>; + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + */ +template<auto Candidate, typename Type> +delegate(connect_arg_t<Candidate>, Type &&) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate), Type>>>; + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template<typename Ret, typename... Args> +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate<Ret(Args...)>; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Sink class. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid signal handler type. + */ +template<typename Type> +class sink; + +/** + * @brief Unmanaged signal handler. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Type, typename Allocator> +class sigh; + +/** + * @brief Unmanaged signal handler. + * + * It works directly with references to classes and pointers to member functions + * as well as pointers to free functions. Users of this class are in charge of + * disconnecting instances before deleting them. + * + * This class serves mainly two purposes: + * + * * Creating signals to use later to notify a bunch of listeners. + * * Collecting results from a set of functions like in a voting system. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Ret, typename... Args, typename Allocator> +class sigh<Ret(Args...), Allocator> { +/*! @brief A sink is allowed to modify a signal. */ +friend class sink<sigh<Ret(Args...), Allocator>>; + +using alloc_traits = std::allocator_traits<Allocator>; +static_assert(std::is_same_v<typename alloc_traits::value_type, Ret (*)(Args...)>, "Invalid value type"); +using container_type = std::vector<delegate<Ret(Args...)>, typename alloc_traits::template rebind_alloc<delegate<Ret(Args...)>>>; + +public: +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Sink type. */ +using sink_type = sink<sigh<Ret(Args...), Allocator>>; + +/*! @brief Default constructor. */ +sigh() +: sigh{allocator_type{}} {} + +/** + * @brief Constructs a signal handler with a given allocator. + * @param allocator The allocator to use. + */ +explicit sigh(const allocator_type &allocator) +: calls{allocator} {} + +/** + * @brief Copy constructor. + * @param other The instance to copy from. + */ +sigh(const sigh &other) +: calls{other.calls} {} + +/** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ +sigh(const sigh &other, const allocator_type &allocator) +: calls{other.calls, allocator} {} + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +sigh(sigh &&other) ENTT_NOEXCEPT +: calls{std::move(other.calls)} {} + +/** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ +sigh(sigh &&other, const allocator_type &allocator) ENTT_NOEXCEPT +: calls{std::move(other.calls), allocator} {} + +/** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This signal handler. + */ +sigh &operator=(const sigh &other) { +calls = other.calls; +return *this; +} + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This signal handler. + */ +sigh &operator=(sigh &&other) ENTT_NOEXCEPT { +calls = std::move(other.calls); +return *this; +} + +/** + * @brief Exchanges the contents with those of a given signal handler. + * @param other Signal handler to exchange the content with. + */ +void swap(sigh &other) { +using std::swap; +swap(calls, other.calls); +} + +/** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ +[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { +return calls.get_allocator(); +} + +/** + * @brief Instance type when it comes to connecting member functions. + * @tparam Class Type of class to which the member function belongs. + */ +template<typename Class> +using instance_type = Class *; + +/** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return calls.size(); +} + +/** + * @brief Returns false if at least a listener is connected to the signal. + * @return True if the signal has no listeners connected, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return calls.empty(); +} + +/** + * @brief Triggers a signal. + * + * All the listeners are notified. Order isn't guaranteed. + * + * @param args Arguments to use to invoke listeners. + */ +void publish(Args... args) const { +for(auto &&call: std::as_const(calls)) { +call(args...); +} +} + +/** + * @brief Collects return values from the listeners. + * + * The collector must expose a call operator with the following properties: + * + * * The return type is either `void` or such that it's convertible to + * `bool`. In the second case, a true value will stop the iteration. + * * The list of parameters is empty if `Ret` is `void`, otherwise it + * contains a single element such that `Ret` is convertible to it. + * + * @tparam Func Type of collector to use, if any. + * @param func A valid function object. + * @param args Arguments to use to invoke listeners. + */ +template<typename Func> +void collect(Func func, Args... args) const { +for(auto &&call: calls) { +if constexpr(std::is_void_v<Ret>) { +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...))) { break; } +} else { +func(call(args...)); +} +} +} +} + +private: +container_type calls; +}; + +/** + * @brief Connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it. + */ +class connection { +/*! @brief A sink is allowed to create connection objects. */ +template<typename> +friend class sink; + +connection(delegate<void(void *)> fn, void *ref) +: disconnect{fn}, signal{ref} {} + +public: +/*! @brief Default constructor. */ +connection() = default; + +/** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(disconnect); +} + +/*! @brief Breaks the connection. */ +void release() { +if(disconnect) { +disconnect(signal); +disconnect.reset(); +} +} + +private: +delegate<void(void *)> disconnect; +void *signal{}; +}; + +/** + * @brief Scoped connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it.<br/> + * A scoped connection automatically breaks the link between the two objects + * when it goes out of scope. + */ +struct scoped_connection { +/*! @brief Default constructor. */ +scoped_connection() = default; + +/** + * @brief Constructs a scoped connection from a basic connection. + * @param other A valid connection object. + */ +scoped_connection(const connection &other) +: conn{other} {} + +/*! @brief Default copy constructor, deleted on purpose. */ +scoped_connection(const scoped_connection &) = delete; + +/** + * @brief Move constructor. + * @param other The scoped connection to move from. + */ +scoped_connection(scoped_connection &&other) ENTT_NOEXCEPT +: conn{std::exchange(other.conn, {})} {} + +/*! @brief Automatically breaks the link on destruction. */ +~scoped_connection() { +conn.release(); +} + +/** + * @brief Default copy assignment operator, deleted on purpose. + * @return This scoped connection. + */ +scoped_connection &operator=(const scoped_connection &) = delete; + +/** + * @brief Move assignment operator. + * @param other The scoped connection to move from. + * @return This scoped connection. + */ +scoped_connection &operator=(scoped_connection &&other) ENTT_NOEXCEPT { +conn = std::exchange(other.conn, {}); +return *this; +} + +/** + * @brief Acquires a connection. + * @param other The connection object to acquire. + * @return This scoped connection. + */ +scoped_connection &operator=(connection other) { +conn = std::move(other); +return *this; +} + +/** + * @brief Checks whether a scoped connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(conn); +} + +/*! @brief Breaks the connection. */ +void release() { +conn.release(); +} + +private: +connection conn; +}; + +/** + * @brief Sink class. + * + * A sink is used to connect listeners to signals and to disconnect them.<br/> + * The function type for a listener is the one of the signal to which it + * belongs. + * + * The clear separation between a signal and a sink permits to store the former + * as private data member without exposing the publish functionality to the + * users of the class. + * + * @warning + * Lifetime of a sink must not overcome that of the signal to which it refers. + * In any other case, attempting to use a sink results in undefined behavior. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Ret, typename... Args, typename Allocator> +class sink<sigh<Ret(Args...), Allocator>> { +using signal_type = sigh<Ret(Args...), Allocator>; +using difference_type = typename signal_type::container_type::difference_type; + +template<auto Candidate, typename Type> +static void release(Type value_or_instance, void *signal) { +sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>(value_or_instance); +} + +template<auto Candidate> +static void release(void *signal) { +sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>(); +} + +public: +/** + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. + */ +sink(sigh<Ret(Args...), Allocator> &ref) ENTT_NOEXCEPT +: offset{}, +signal{&ref} {} + +/** + * @brief Returns false if at least a listener is connected to the sink. + * @return True if the sink has no listeners connected, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +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>(); + +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; +} + +/** + * @brief Returns a sink that connects before a free function with payload + * or a bound member. + * @tparam Candidate Member or free function to look for. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ +template<auto Candidate, typename Type> +[[nodiscard]] sink before(Type &&value_or_instance) { +delegate<Ret(Args...)> call{}; +call.template connect<Candidate>(value_or_instance); + +const auto &calls = signal->calls; +const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + +sink other{*this}; +other.offset = calls.cend() - it; +return other; +} + +/** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ +template<typename Type> +[[nodiscard]] sink before(Type &value_or_instance) { +return before(&value_or_instance); +} + +/** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + * @return A properly initialized sink object. + */ +template<typename Type> +[[nodiscard]] sink before(Type *value_or_instance) { +sink other{*this}; + +if(value_or_instance) { +const auto &calls = signal->calls; +const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) { +return delegate.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 or an unbound member to a signal. + * + * The signal handler performs checks to avoid multiple connections for the + * same function. + * + * @tparam Candidate Function or member to connect to the signal. + * @return A properly initialized connection object. + */ +template<auto Candidate> +connection connect() { +disconnect<Candidate>(); + +delegate<Ret(Args...)> call{}; +call.template connect<Candidate>(); +signal->calls.insert(signal->calls.end() - offset, std::move(call)); + +delegate<void(void *)> conn{}; +conn.template connect<&release<Candidate>>(); +return {std::move(conn), signal}; +} + +/** + * @brief Connects a free function with payload or a bound member to a + * signal. + * + * The signal isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the 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. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized connection object. + */ +template<auto Candidate, typename Type> +connection connect(Type &&value_or_instance) { +disconnect<Candidate>(value_or_instance); + +delegate<Ret(Args...)> call{}; +call.template connect<Candidate>(value_or_instance); +signal->calls.insert(signal->calls.end() - offset, std::move(call)); + +delegate<void(void *)> conn{}; +conn.template connect<&release<Candidate, Type>>(value_or_instance); +return {std::move(conn), signal}; +} + +/** + * @brief Disconnects a free function or an unbound member from a signal. + * @tparam Candidate Function or member to disconnect from the signal. + */ +template<auto Candidate> +void disconnect() { +auto &calls = signal->calls; +delegate<Ret(Args...)> call{}; +call.template connect<Candidate>(); +calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); +} + +/** + * @brief Disconnects a free function with payload or a bound member from a + * signal. + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ +template<auto Candidate, typename Type> +void disconnect(Type &&value_or_instance) { +auto &calls = signal->calls; +delegate<Ret(Args...)> call{}; +call.template connect<Candidate>(value_or_instance); +calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); +} + +/** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ +template<typename Type> +void disconnect(Type &value_or_instance) { +disconnect(&value_or_instance); +} + +/** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ +template<typename Type> +void disconnect(Type *value_or_instance) { +if(value_or_instance) { +auto &calls = signal->calls; +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()); +} +} + +/*! @brief Disconnects all the listeners from a signal. */ +void disconnect() { +signal->calls.clear(); +} + +private: +difference_type offset; +signal_type *signal; +}; + +/** + * @brief Deduction guide. + * + * It allows to deduce the signal handler type of a sink directly from the + * signal it refers to. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Ret, typename... Args, typename Allocator> +sink(sigh<Ret(Args...), Allocator> &) -> sink<sigh<Ret(Args...), Allocator>>; + +} // namespace entt + +#endif + +// #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_iterator = typename Type::basic_iterator; + +template<typename Func> +void notify_destruction(basic_iterator first, basic_iterator last, Func func) { +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); +func(it, it + 1u); +} +} + +void swap_and_pop(basic_iterator first, basic_iterator last) final { +notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::swap_and_pop(args...); }); +} + +void in_place_pop(basic_iterator first, basic_iterator last) final { +notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::in_place_pop(args...); }); +} + +basic_iterator try_emplace(const typename 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 Underlying entity identifier. */ +using entity_type = typename Type::entity_type; + +/*! @brief Inherited constructors. */ +using Type::Type; + +/** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever a new instance is created and assigned to an entity.<br/> + * Listeners are invoked after the object has been assigned to the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ +[[nodiscard]] auto on_construct() ENTT_NOEXCEPT { +return sink{construction}; +} + +/** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is explicitly updated.<br/> + * Listeners are invoked after the object has been updated. + * + * @sa sink + * + * @return A temporary sink object. + */ +[[nodiscard]] auto on_update() ENTT_NOEXCEPT { +return sink{update}; +} + +/** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is removed from an entity and thus destroyed.<br/> + * Listeners are invoked before the object has been removed from the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ +[[nodiscard]] auto on_destroy() ENTT_NOEXCEPT { +return sink{destruction}; +} + +/** + * @brief Assigns entities to a storage. + * @tparam Args Types of arguments to use to construct the object. + * @param 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 mixins, if any. + * @param value A variable wrapped in an opaque container. + */ +void bind(any value) ENTT_NOEXCEPT final { +auto *reg = any_cast<basic_registry<entity_type>>(&value); +owner = reg ? reg : owner; +Type::bind(std::move(value)); +} + +private: +sigh<void(basic_registry<entity_type> &, const entity_type)> construction{}; +sigh<void(basic_registry<entity_type> &, const entity_type)> destruction{}; +sigh<void(basic_registry<entity_type> &, const entity_type)> update{}; +basic_registry<entity_type> *owner{}; +}; + +} // namespace entt + +#endif + +// #include "sparse_set.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Container> +class storage_iterator final { +friend storage_iterator<const Container>; + +using container_type = std::remove_const_t<Container>; +using alloc_traits = std::allocator_traits<typename container_type::allocator_type>; +using comp_traits = component_traits<typename container_type::value_type>; + +using iterator_traits = std::iterator_traits<std::conditional_t< +std::is_const_v<Container>, +typename alloc_traits::template rebind_traits<typename std::pointer_traits<typename container_type::value_type>::element_type>::const_pointer, +typename alloc_traits::template rebind_traits<typename std::pointer_traits<typename container_type::value_type>::element_type>::pointer>>; + +public: +using value_type = typename iterator_traits::value_type; +using pointer = typename iterator_traits::pointer; +using reference = typename iterator_traits::reference; +using difference_type = typename iterator_traits::difference_type; +using iterator_category = std::random_access_iterator_tag; + +storage_iterator() ENTT_NOEXCEPT = default; + +storage_iterator(Container *ref, difference_type idx) ENTT_NOEXCEPT +: packed{ref}, +offset{idx} {} + +template<bool Const = std::is_const_v<Container>, typename = std::enable_if_t<Const>> +storage_iterator(const storage_iterator<std::remove_const_t<Container>> &other) ENTT_NOEXCEPT +: packed{other.packed}, +offset{other.offset} {} + +storage_iterator &operator++() ENTT_NOEXCEPT { +return --offset, *this; +} + +storage_iterator operator++(int) ENTT_NOEXCEPT { +storage_iterator orig = *this; +return ++(*this), orig; +} + +storage_iterator &operator--() ENTT_NOEXCEPT { +return ++offset, *this; +} + +storage_iterator operator--(int) ENTT_NOEXCEPT { +storage_iterator orig = *this; +return operator--(), orig; +} + +storage_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { +offset -= value; +return *this; +} + +storage_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { +storage_iterator copy = *this; +return (copy += value); +} + +storage_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { +return (*this += -value); +} + +storage_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { +return (*this + -value); +} + +[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { +const auto pos = index() - value; +return (*packed)[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)]; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +const auto pos = index(); +return (*packed)[pos / comp_traits::page_size] + fast_mod(pos, comp_traits::page_size); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return *operator->(); +} + +[[nodiscard]] difference_type index() const ENTT_NOEXCEPT { +return offset - 1; +} + +private: +Container *packed; +difference_type offset; +}; + +template<typename CLhs, typename CRhs> +[[nodiscard]] std::ptrdiff_t operator-(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT { +return rhs.index() - lhs.index(); +} + +template<typename CLhs, typename CRhs> +[[nodiscard]] bool operator==(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT { +return lhs.index() == rhs.index(); +} + +template<typename CLhs, typename CRhs> +[[nodiscard]] bool operator!=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +template<typename CLhs, typename CRhs> +[[nodiscard]] bool operator<(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT { +return lhs.index() > rhs.index(); +} + +template<typename CLhs, typename CRhs> +[[nodiscard]] bool operator>(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT { +return lhs.index() < rhs.index(); +} + +template<typename CLhs, typename CRhs> +[[nodiscard]] bool operator<=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs > rhs); +} + +template<typename CLhs, typename CRhs> +[[nodiscard]] bool operator>=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +template<typename It, typename... Other> +class extended_storage_iterator final { +template<typename Iter, typename... Args> +friend class extended_storage_iterator; + +public: +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; + +extended_storage_iterator() = default; + +extended_storage_iterator(It base, Other... other) +: it{base, other...} {} + +template<typename... Args, typename = std::enable_if_t<(!std::is_same_v<Other, Args> && ...) && (std::is_constructible_v<Other, Args> && ...)>> +extended_storage_iterator(const extended_storage_iterator<It, Args...> &other) +: it{other.it} {} + +extended_storage_iterator &operator++() ENTT_NOEXCEPT { +return ++std::get<It>(it), (++std::get<Other>(it), ...), *this; +} + +extended_storage_iterator operator++(int) ENTT_NOEXCEPT { +extended_storage_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return {*std::get<It>(it), *std::get<Other>(it)...}; +} + +template<typename... CLhs, typename... CRhs> +friend bool operator==(const extended_storage_iterator<CLhs...> &, const extended_storage_iterator<CRhs...> &) ENTT_NOEXCEPT; + +private: +std::tuple<It, Other...> it; +}; + +template<typename... CLhs, typename... CRhs> +[[nodiscard]] bool operator==(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) ENTT_NOEXCEPT { +return std::get<0>(lhs.it) == std::get<0>(rhs.it); +} + +template<typename... CLhs, typename... CRhs> +[[nodiscard]] bool operator!=(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Basic storage implementation. + * + * Internal data structures arrange elements to maximize performance. There are + * no guarantees that objects are returned in the insertion order when iterate + * a storage. Do not make assumption on the order in any case. + * + * @warning + * Empty types aren't explicitly instantiated. Therefore, many of the functions + * normally available for non-empty types will not be available for empty ones. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Type of objects assigned to the entities. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Entity, typename Type, typename Allocator, typename> +class basic_storage: public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> { +static_assert(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>, "The type must be at least move constructible/assignable"); + +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>; + +[[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)]; +} + +auto assure_at_least(const std::size_t pos) { +auto &&container = packed.first(); +const auto idx = pos / comp_traits::page_size; + +if(!(idx < container.size())) { +auto curr = container.size(); +container.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); +} +} +ENTT_CATCH { +container.resize(curr); +ENTT_THROW; +} +} + +return container[idx] + fast_mod(pos, comp_traits::page_size); +} + +template<typename... Args> +auto emplace_element(const Entity entt, const bool force_back, Args &&...args) { +const auto it = base_type::try_emplace(entt, force_back); + +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_CATCH { +if constexpr(comp_traits::in_place_delete) { +base_type::in_place_pop(it, it + 1u); +} else { +base_type::swap_and_pop(it, it + 1u); +} + +ENTT_THROW; +} + +return it; +} + +void shrink_to_size(const std::size_t sz) { +for(auto pos = sz, length = base_type::size(); pos < length; ++pos) { +if constexpr(comp_traits::in_place_delete) { +if(base_type::at(pos) != tombstone) { +std::destroy_at(std::addressof(element_at(pos))); +} +} else { +std::destroy_at(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); +} + +container.resize(from); +} + +private: +const void *get_at(const std::size_t pos) const final { +return std::addressof(element_at(pos)); +} + +void swap_at(const std::size_t lhs, const std::size_t rhs) final { +using std::swap; +swap(element_at(lhs), element_at(rhs)); +} + +void move_element(const std::size_t from, const std::size_t to) final { +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)); +} + +protected: +/** + * @brief Erases elements from a storage. + * @param first An iterator to the first element to erase. + * @param last An iterator past the last element to erase. + */ +void swap_and_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override { +for(; first != last; ++first) { +auto &elem = element_at(base_type::size() - 1u); +// destroying on exit allows reentrant destructors +[[maybe_unused]] auto unused = std::exchange(element_at(static_cast<size_type>(first.index())), std::move(elem)); +std::destroy_at(std::addressof(elem)); +base_type::swap_and_pop(first, first + 1u); +} +} + +/** + * @brief Erases elements from a storage. + * @param first An iterator to the first element to erase. + * @param last An iterator past the last element to erase. + */ +void in_place_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override { +for(; first != last; ++first) { +base_type::in_place_pop(first, first + 1u); +std::destroy_at(std::addressof(element_at(static_cast<size_type>(first.index())))); +} +} + +/** + * @brief Assigns an entity to a storage. + * @param entt A valid identifier. + * @param value Optional opaque value. + * @param force_back Force back insertion. + * @return Iterator pointing to the emplaced element. + */ +typename underlying_type::basic_iterator try_emplace([[maybe_unused]] const Entity entt, 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)); +} else { +return base_type::end(); +} +} else { +if constexpr(std::is_default_constructible_v<value_type>) { +return emplace_element(entt, force_back); +} else { +return base_type::end(); +} +} +} + +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 Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @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>; +/*! @brief Constant random access iterator type. */ +using const_iterator = internal::storage_iterator<const container_type>; +/*! @brief Reverse iterator type. */ +using reverse_iterator = std::reverse_iterator<iterator>; +/*! @brief Constant reverse iterator type. */ +using const_reverse_iterator = std::reverse_iterator<const_iterator>; +/*! @brief Extended iterable storage proxy. */ +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 Default constructor. */ +basic_storage() +: basic_storage{allocator_type{}} {} + +/** + * @brief Constructs an empty storage 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{comp_traits::in_place_delete}, allocator}, +packed{container_type{allocator}, allocator} {} + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +basic_storage(basic_storage &&other) ENTT_NOEXCEPT +: base_type{std::move(other)}, +packed{std::move(other.packed)} {} + +/** + * @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) ENTT_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"); +} + +/*! @brief Default destructor. */ +~basic_storage() override { +shrink_to_size(0u); +} + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ +basic_storage &operator=(basic_storage &&other) ENTT_NOEXCEPT { +ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "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()); +return *this; +} + +/** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ +void swap(basic_storage &other) { +using std::swap; +underlying_type::swap(other); +propagate_on_container_swap(packed.second(), other.packed.second()); +swap(packed.first(), other.packed.first()); +} + +/** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ +[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { +return allocator_type{packed.second()}; +} + +/** + * @brief Increases the capacity of a storage. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ +void reserve(const size_type cap) override { +if(cap != 0u) { +base_type::reserve(cap); +assure_at_least(cap - 1u); +} +} + +/** + * @brief Returns the number of elements that a storage has currently + * allocated space for. + * @return Capacity of the storage. + */ +[[nodiscard]] size_type capacity() const ENTT_NOEXCEPT override { +return packed.first().size() * comp_traits::page_size; +} + +/*! @brief Requests the removal of unused capacity. */ +void shrink_to_fit() override { +base_type::shrink_to_fit(); +shrink_to_size(base_type::size()); +} + +/** + * @brief Direct access to the array of objects. + * @return A pointer to the array of objects. + */ +[[nodiscard]] const_pointer raw() const ENTT_NOEXCEPT { +return packed.first().data(); +} + +/*! @copydoc raw */ +[[nodiscard]] pointer raw() ENTT_NOEXCEPT { +return packed.first().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 ENTT_NOEXCEPT { +const auto pos = static_cast<typename iterator::difference_type>(base_type::size()); +return const_iterator{&packed.first(), pos}; +} + +/*! @copydoc cbegin */ +[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { +return cbegin(); +} + +/*! @copydoc begin */ +[[nodiscard]] iterator begin() ENTT_NOEXCEPT { +const auto pos = static_cast<typename iterator::difference_type>(base_type::size()); +return iterator{&packed.first(), pos}; +} + +/** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the internal array. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the element following the last instance of the + * internal array. + */ +[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { +return const_iterator{&packed.first(), {}}; +} + +/*! @copydoc cend */ +[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { +return cend(); +} + +/*! @copydoc end */ +[[nodiscard]] iterator end() ENTT_NOEXCEPT { +return iterator{&packed.first(), {}}; +} + +/** + * @brief Returns a reverse iterator to the beginning. + * + * The returned iterator points to the first instance of the reversed + * internal array. If the storage is empty, the returned iterator will be + * equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ +[[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT { +return std::make_reverse_iterator(cend()); +} + +/*! @copydoc crbegin */ +[[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT { +return crbegin(); +} + +/*! @copydoc rbegin */ +[[nodiscard]] reverse_iterator rbegin() ENTT_NOEXCEPT { +return std::make_reverse_iterator(end()); +} + +/** + * @brief Returns a reverse iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the reversed internal array. Attempting to dereference the returned + * iterator results in undefined behavior. + * + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ +[[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT { +return std::make_reverse_iterator(cbegin()); +} + +/*! @copydoc crend */ +[[nodiscard]] const_reverse_iterator rend() const ENTT_NOEXCEPT { +return crend(); +} + +/*! @copydoc rend */ +[[nodiscard]] reverse_iterator rend() ENTT_NOEXCEPT { +return std::make_reverse_iterator(begin()); +} + +/** + * @brief Returns the object assigned to an entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return The object assigned to the entity. + */ +[[nodiscard]] const value_type &get(const entity_type entt) const ENTT_NOEXCEPT { +return element_at(base_type::index(entt)); +} + +/*! @copydoc get */ +[[nodiscard]] value_type &get(const entity_type entt) ENTT_NOEXCEPT { +return const_cast<value_type &>(std::as_const(*this).get(entt)); +} + +/** + * @brief Returns the object assigned to an entity as a tuple. + * @param entt A valid identifier. + * @return The object assigned to the entity as a tuple. + */ +[[nodiscard]] std::tuple<const value_type &> get_as_tuple(const entity_type entt) const ENTT_NOEXCEPT { +return std::forward_as_tuple(get(entt)); +} + +/*! @copydoc get_as_tuple */ +[[nodiscard]] std::tuple<value_type &> get_as_tuple(const entity_type entt) ENTT_NOEXCEPT { +return std::forward_as_tuple(get(entt)); +} + +/** + * @brief Assigns an entity to a storage and constructs its object. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + * @param args Parameters to use to construct an object for the entity. + * @return A reference to the newly created object. + */ +template<typename... Args> +value_type &emplace(const entity_type entt, Args &&...args) { +if constexpr(std::is_aggregate_v<value_type>) { +const auto it = emplace_element(entt, false, Type{std::forward<Args>(args)...}); +return element_at(static_cast<size_type>(it.index())); +} else { +const auto it = emplace_element(entt, false, std::forward<Args>(args)...); +return element_at(static_cast<size_type>(it.index())); +} +} + +/** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the updated instance. + */ +template<typename... Func> +value_type &patch(const entity_type entt, Func &&...func) { +const auto idx = base_type::index(entt); +auto &elem = element_at(idx); +(std::forward<Func>(func)(elem), ...); +return elem; +} + +/** + * @brief Assigns one or more entities to a storage and constructs their + * objects from a given instance. + * + * @warning + * Attempting to assign an entity that already belongs to the storage + * results in undefined behavior. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param value An instance of the object to construct. + */ +template<typename It> +void insert(It first, It last, const value_type &value = {}) { +for(; first != last; ++first) { +emplace_element(*first, true, value); +} +} + +/** + * @brief Assigns one or more entities to a storage and constructs their + * objects from a given range. + * + * @sa construct + * + * @tparam EIt Type of input iterator. + * @tparam CIt Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param from An iterator to the first element of the range of objects. + */ +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) { +for(; first != last; ++first, ++from) { +emplace_element(*first, true, *from); +} +} + +/** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity and + * a reference to its component. + * + * @return An iterable object to use to _visit_ the storage. + */ +[[nodiscard]] iterable each() ENTT_NOEXCEPT { +return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}}; +} + +/*! @copydoc each */ +[[nodiscard]] const_iterable each() const ENTT_NOEXCEPT { +return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}}; +} + +private: +compressed_pair<container_type, allocator_type> packed; +}; + +/*! @copydoc basic_storage */ +template<typename Entity, typename Type, typename Allocator> +class basic_storage<Entity, Type, Allocator, std::enable_if_t<ignore_as_empty_v<Type>>> +: 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; +/*! @brief Type of the objects assigned to entities. */ +using value_type = Type; +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief 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 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{comp_traits::in_place_delete}, allocator} {} + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +basic_storage(basic_storage &&other) ENTT_NOEXCEPT = default; + +/** + * @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) ENTT_NOEXCEPT +: base_type{std::move(other), allocator} {} + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ +basic_storage &operator=(basic_storage &&other) ENTT_NOEXCEPT = default; + +/** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ +[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { +return allocator_type{base_type::get_allocator()}; +} + +/** + * @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 ENTT_NOEXCEPT { +ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); +} + +/** + * @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 ENTT_NOEXCEPT { +ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); +return std::tuple{}; +} + +/** + * @brief Assigns an entity to a storage and constructs its object. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + */ +template<typename... Args> +void emplace(const entity_type entt, Args &&...) { +base_type::try_emplace(entt, false); +} + +/** + * @brief Updates the instance assigned to a given entity in-place. + * @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 entities to a storage. + * @tparam It Type of input iterator. + * @tparam Args Types of optional arguments. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ +template<typename It, typename... Args> +void insert(It first, It last, Args &&...) { +for(; first != last; ++first) { +base_type::try_emplace(*first, true); +} +} + +/** + * @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() ENTT_NOEXCEPT { +return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}}; +} + +/*! @copydoc each */ +[[nodiscard]] const_iterable each() const ENTT_NOEXCEPT { +return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}}; +} +}; + +/** + * @brief Provides a common way to access certain properties of storage types. + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Type of objects managed by the storage class. + */ +template<typename Entity, typename Type, typename = void> +struct storage_traits { +/*! @brief Resulting type after component-to-storage conversion. */ +using storage_type = sigh_storage_mixin<basic_storage<Entity, Type>>; +}; + +} // namespace entt + +#endif + +// #include "utility.hpp" + + +namespace entt { + +/** + * @brief Group. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template<typename, typename, typename, typename> +class basic_group; + +/** + * @brief Non-owning group. + * + * A non-owning group returns all entities and only the entities that have at + * least the given components. Moreover, it's guaranteed that the entity list + * is tightly packed in memory for fast iterations. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Groups share references to the underlying data structures of the registry + * that generated them. Therefore any change to the entities and to the + * components made by means of the registry are immediately reflected by all the + * groups.<br/> + * Moreover, sorting a non-owning group affects all the instances of the same + * group (it means that users don't have to call `sort` on each instance to sort + * all of them because they _share_ entities and components). + * + * @warning + * Lifetime of a group must not overcome that of the registry that generated it. + * In any other case, attempting to use a group results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Get Type of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + */ +template<typename Entity, typename... Get, typename... Exclude> +class basic_group<Entity, owned_t<>, get_t<Get...>, exclude_t<Exclude...>> { +/*! @brief A registry is allowed to create groups. */ +friend class basic_registry<Entity>; + +template<typename Comp> +using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>; + +using basic_common_type = std::common_type_t<typename storage_type<Get>::base_type...>; + +struct extended_group_iterator final { +using difference_type = std::ptrdiff_t; +using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({}))); +using pointer = input_iterator_pointer<value_type>; +using reference = value_type; +using iterator_category = std::input_iterator_tag; + +extended_group_iterator() = default; + +extended_group_iterator(typename basic_common_type::iterator from, const std::tuple<storage_type<Get> *...> &args) +: it{from}, +pools{args} {} + +extended_group_iterator &operator++() ENTT_NOEXCEPT { +return ++it, *this; +} + +extended_group_iterator operator++(int) ENTT_NOEXCEPT { +extended_group_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +const auto entt = *it; +return std::tuple_cat(std::make_tuple(entt), std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...); +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +[[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT { +return other.it == it; +} + +[[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT { +return !(*this == other); +} + +private: +typename basic_common_type::iterator it; +std::tuple<storage_type<Get> *...> pools; +}; + +basic_group(basic_common_type &ref, storage_type<Get> &...gpool) ENTT_NOEXCEPT +: handler{&ref}, +pools{&gpool...} {} + +public: +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Common type among all storage types. */ +using base_type = basic_common_type; +/*! @brief Random access iterator type. */ +using iterator = typename base_type::iterator; +/*! @brief Reversed iterator type. */ +using reverse_iterator = typename base_type::reverse_iterator; +/*! @brief Iterable group type. */ +using iterable = iterable_adaptor<extended_group_iterator>; + +/*! @brief Default constructor to use to create empty, invalid groups. */ +basic_group() ENTT_NOEXCEPT +: handler{} {} + +/** + * @brief Returns a const reference to the underlying handler. + * @return A const reference to the underlying handler. + */ +const base_type &handle() const ENTT_NOEXCEPT { +return *handler; +} + +/** + * @brief Returns the storage for a given component type. + * @tparam Comp Type of component of which to return the storage. + * @return The storage for the given component type. + */ +template<typename Comp> +[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { +return *std::get<storage_type<Comp> *>(pools); +} + +/** + * @brief Returns the storage for a given component type. + * @tparam Comp Index of component of which to return the storage. + * @return The storage for the given component type. + */ +template<std::size_t Comp> +[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { +return *std::get<Comp>(pools); +} + +/** + * @brief Returns the number of entities that have the given components. + * @return Number of entities that have the given components. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return *this ? handler->size() : size_type{}; +} + +/** + * @brief Returns the number of elements that a group has currently + * allocated space for. + * @return Capacity of the group. + */ +[[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { +return *this ? handler->capacity() : size_type{}; +} + +/*! @brief Requests the removal of unused capacity. */ +void shrink_to_fit() { +if(*this) { +handler->shrink_to_fit(); +} +} + +/** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return !*this || handler->empty(); +} + +/** + * @brief Returns an iterator to the first entity of the group. + * + * The returned iterator points to the first entity of the group. If the + * group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return *this ? handler->begin() : iterator{}; +} + +/** + * @brief Returns an iterator that is past the last entity of the group. + * + * The returned iterator points to the entity following the last entity of + * the group. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * group. + */ +[[nodiscard]] iterator end() const ENTT_NOEXCEPT { +return *this ? handler->end() : iterator{}; +} + +/** + * @brief Returns an iterator to the first entity of the reversed group. + * + * The returned iterator points to the first entity of the reversed group. + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ +[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { +return *this ? handler->rbegin() : reverse_iterator{}; +} + +/** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * + * The returned iterator points to the entity following the last entity of + * the reversed group. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * reversed group. + */ +[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { +return *this ? handler->rend() : reverse_iterator{}; +} + +/** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ +[[nodiscard]] entity_type front() const ENTT_NOEXCEPT { +const auto it = begin(); +return it != end() ? *it : null; +} + +/** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ +[[nodiscard]] entity_type back() const ENTT_NOEXCEPT { +const auto it = rbegin(); +return it != rend() ? *it : null; +} + +/** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ +[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT { +const auto it = *this ? handler->find(entt) : iterator{}; +return it != end() && *it == entt ? it : end(); +} + +/** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ +[[nodiscard]] entity_type operator[](const size_type pos) const { +return begin()[pos]; +} + +/** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return handler != nullptr; +} + +/** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ +[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { +return *this && handler->contains(entt); +} + +/** + * @brief Returns the components assigned to the given entity. + * + * Prefer this function instead of `registry::get` during iterations. It has + * far better performance than its counterpart. + * + * @warning + * Attempting to use an invalid component type results in a compilation + * error. Attempting to use an entity that doesn't belong to the group + * results in undefined behavior. + * + * @tparam Comp Types of components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ +template<typename... Comp> +[[nodiscard]] decltype(auto) get(const entity_type entt) const { +ENTT_ASSERT(contains(entt), "Group does not contain entity"); + +if constexpr(sizeof...(Comp) == 0) { +return std::tuple_cat(std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...); +} else if constexpr(sizeof...(Comp) == 1) { +return (std::get<storage_type<Comp> *>(pools)->get(entt), ...); +} else { +return std::tuple_cat(std::get<storage_type<Comp> *>(pools)->get_as_tuple(entt)...); +} +} + +/** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.<br/> + * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ +template<typename Func> +void each(Func func) const { +for(const auto entt: *this) { +if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) { +std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); +} else { +std::apply(func, get(entt)); +} +} +} + +/** + * @brief Returns an iterable object to use to _visit_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ +[[nodiscard]] iterable each() const ENTT_NOEXCEPT { +return handler ? iterable{extended_group_iterator{handler->begin(), pools}, extended_group_iterator{handler->end(), pools}} +: iterable{extended_group_iterator{{}, pools}, extended_group_iterator{{}, pools}}; +} + +/** + * @brief Sort a group according to the given comparison function. + * + * Sort the group so that iterating it with a couple of iterators returns + * entities and components in the expected order. See `begin` and `end` for + * more details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple<Component &...>, std::tuple<Component &...>); + * bool(const Component &..., const Component &...); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Component` are such that they are iterated by the group.<br/> + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function object must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Comp Optional types of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ +template<typename... Comp, typename Compare, typename Sort = std_sort, typename... Args> +void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { +if(*this) { +if constexpr(sizeof...(Comp) == 0) { +static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function"); +handler->sort(std::move(compare), std::move(algo), std::forward<Args>(args)...); +} else { +auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) { +if constexpr(sizeof...(Comp) == 1) { +return compare((std::get<storage_type<Comp> *>(pools)->get(lhs), ...), (std::get<storage_type<Comp> *>(pools)->get(rhs), ...)); +} else { +return compare(std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(lhs)...), std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(rhs)...)); +} +}; + +handler->sort(std::move(comp), std::move(algo), std::forward<Args>(args)...); +} +} +} + +/** + * @brief Sort the shared pool of entities according to the given component. + * + * Non-owning groups of the same type share with the registry a pool of + * entities with its own order that doesn't depend on the order of any pool + * of components. Users can order the underlying data structure so that it + * respects the order of the pool of the given component. + * + * @note + * The shared pool of entities and thus its order is affected by the changes + * to each and every pool that it tracks. Therefore changes to those pools + * can quickly ruin the order imposed to the pool of entities shared between + * the non-owning groups. + * + * @tparam Comp Type of component to use to impose the order. + */ +template<typename Comp> +void sort() const { +if(*this) { +handler->respect(*std::get<storage_type<Comp> *>(pools)); +} +} + +private: +base_type *const handler; +const std::tuple<storage_type<Get> *...> pools; +}; + +/** + * @brief Owning group. + * + * Owning groups return all entities and only the entities that have at least + * the given components. Moreover: + * + * * It's guaranteed that the entity list is tightly packed in memory for fast + * iterations. + * * It's guaranteed that the lists of owned components are tightly packed in + * memory for even faster iterations and to allow direct access. + * * They stay true to the order of the owned components and all instances have + * the same order in memory. + * + * The more types of components are owned by a group, the faster it is to + * iterate them. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Groups share references to the underlying data structures of the registry + * that generated them. Therefore any change to the entities and to the + * components made by means of the registry are immediately reflected by all the + * groups. + * Moreover, sorting an owning group affects all the instance of the same group + * (it means that users don't have to call `sort` on each instance to sort all + * of them because they share the underlying data structure). + * + * @warning + * Lifetime of a group must not overcome that of the registry that generated it. + * In any other case, attempting to use a group results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Owned Types of components owned by the group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + */ +template<typename Entity, typename... Owned, typename... Get, typename... Exclude> +class basic_group<Entity, owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> { +/*! @brief A registry is allowed to create groups. */ +friend class basic_registry<Entity>; + +template<typename Comp> +using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>; + +using basic_common_type = std::common_type_t<typename storage_type<Owned>::base_type..., typename storage_type<Get>::base_type...>; + +class extended_group_iterator final { +template<typename Type> +auto index_to_element(storage_type<Type> &cpool) const { +if constexpr(ignore_as_empty_v<std::remove_const_t<Type>>) { +return std::make_tuple(); +} else { +return std::forward_as_tuple(cpool.rbegin()[it.index()]); +} +} + +public: +using difference_type = std::ptrdiff_t; +using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({}))); +using pointer = input_iterator_pointer<value_type>; +using reference = value_type; +using iterator_category = std::input_iterator_tag; + +extended_group_iterator() = default; + +template<typename... Other> +extended_group_iterator(typename basic_common_type::iterator from, const std::tuple<storage_type<Owned> *..., storage_type<Get> *...> &cpools) +: it{from}, +pools{cpools} {} + +extended_group_iterator &operator++() ENTT_NOEXCEPT { +return ++it, *this; +} + +extended_group_iterator operator++(int) ENTT_NOEXCEPT { +extended_group_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return std::tuple_cat( +std::make_tuple(*it), +index_to_element<Owned>(*std::get<storage_type<Owned> *>(pools))..., +std::get<storage_type<Get> *>(pools)->get_as_tuple(*it)...); +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +[[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT { +return other.it == it; +} + +[[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT { +return !(*this == other); +} + +private: +typename basic_common_type::iterator it; +std::tuple<storage_type<Owned> *..., storage_type<Get> *...> pools; +}; + +basic_group(const std::size_t &extent, storage_type<Owned> &...opool, storage_type<Get> &...gpool) ENTT_NOEXCEPT +: pools{&opool..., &gpool...}, +length{&extent} {} + +public: +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Common type among all storage types. */ +using base_type = basic_common_type; +/*! @brief Random access iterator type. */ +using iterator = typename base_type::iterator; +/*! @brief Reversed iterator type. */ +using reverse_iterator = typename base_type::reverse_iterator; +/*! @brief Iterable group type. */ +using iterable = iterable_adaptor<extended_group_iterator>; + +/*! @brief Default constructor to use to create empty, invalid groups. */ +basic_group() ENTT_NOEXCEPT +: length{} {} + +/** + * @brief Returns the storage for a given component type. + * @tparam Comp Type of component of which to return the storage. + * @return The storage for the given component type. + */ +template<typename Comp> +[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { +return *std::get<storage_type<Comp> *>(pools); +} + +/** + * @brief Returns the storage for a given component type. + * @tparam Comp Index of component of which to return the storage. + * @return The storage for the given component type. + */ +template<std::size_t Comp> +[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { +return *std::get<Comp>(pools); +} + +/** + * @brief Returns the number of entities that have the given components. + * @return Number of entities that have the given components. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return *this ? *length : size_type{}; +} + +/** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return !*this || !*length; +} + +/** + * @brief Returns an iterator to the first entity of the group. + * + * The returned iterator points to the first entity of the group. If the + * group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return *this ? (std::get<0>(pools)->base_type::end() - *length) : iterator{}; +} + +/** + * @brief Returns an iterator that is past the last entity of the group. + * + * The returned iterator points to the entity following the last entity of + * the group. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * group. + */ +[[nodiscard]] iterator end() const ENTT_NOEXCEPT { +return *this ? std::get<0>(pools)->base_type::end() : iterator{}; +} + +/** + * @brief Returns an iterator to the first entity of the reversed group. + * + * The returned iterator points to the first entity of the reversed group. + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ +[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { +return *this ? std::get<0>(pools)->base_type::rbegin() : reverse_iterator{}; +} + +/** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * + * The returned iterator points to the entity following the last entity of + * the reversed group. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * reversed group. + */ +[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { +return *this ? (std::get<0>(pools)->base_type::rbegin() + *length) : reverse_iterator{}; +} + +/** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ +[[nodiscard]] entity_type front() const ENTT_NOEXCEPT { +const auto it = begin(); +return it != end() ? *it : null; +} + +/** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ +[[nodiscard]] entity_type back() const ENTT_NOEXCEPT { +const auto it = rbegin(); +return it != rend() ? *it : null; +} + +/** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ +[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT { +const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{}; +return it != end() && it >= begin() && *it == entt ? it : end(); +} + +/** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ +[[nodiscard]] entity_type operator[](const size_type pos) const { +return begin()[pos]; +} + +/** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return length != nullptr; +} + +/** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ +[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { +return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length)); +} + +/** + * @brief Returns the components assigned to the given entity. + * + * Prefer this function instead of `registry::get` during iterations. It has + * far better performance than its counterpart. + * + * @warning + * Attempting to use an invalid component type results in a compilation + * error. Attempting to use an entity that doesn't belong to the group + * results in undefined behavior. + * + * @tparam Comp Types of components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ +template<typename... Comp> +[[nodiscard]] decltype(auto) get(const entity_type entt) const { +ENTT_ASSERT(contains(entt), "Group does not contain entity"); + +if constexpr(sizeof...(Comp) == 0) { +return std::tuple_cat(std::get<storage_type<Owned> *>(pools)->get_as_tuple(entt)..., std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...); +} else if constexpr(sizeof...(Comp) == 1) { +return (std::get<storage_type<Comp> *>(pools)->get(entt), ...); +} else { +return std::tuple_cat(std::get<storage_type<Comp> *>(pools)->get_as_tuple(entt)...); +} +} + +/** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.<br/> + * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ +template<typename Func> +void each(Func func) const { +for(auto args: each()) { +if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) { +std::apply(func, args); +} else { +std::apply([&func](auto, auto &&...less) { func(std::forward<decltype(less)>(less)...); }, args); +} +} +} + +/** + * @brief Returns an iterable object to use to _visit_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ +[[nodiscard]] iterable each() const ENTT_NOEXCEPT { +iterator last = length ? std::get<0>(pools)->basic_common_type::end() : iterator{}; +return {extended_group_iterator{last - *length, pools}, extended_group_iterator{last, pools}}; +} + +/** + * @brief Sort a group according to the given comparison function. + * + * Sort the group so that iterating it with a couple of iterators returns + * entities and components in the expected order. See `begin` and `end` for + * more details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple<Component &...>, std::tuple<Component &...>); + * bool(const Component &, const Component &); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Component` are either owned types or not but still such that they + * are iterated by the group.<br/> + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function object must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Comp Optional types of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ +template<typename... Comp, typename Compare, typename Sort = std_sort, typename... Args> +void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const { +auto *cpool = std::get<0>(pools); + +if constexpr(sizeof...(Comp) == 0) { +static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function"); +cpool->sort_n(*length, std::move(compare), std::move(algo), std::forward<Args>(args)...); +} else { +auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) { +if constexpr(sizeof...(Comp) == 1) { +return compare((std::get<storage_type<Comp> *>(pools)->get(lhs), ...), (std::get<storage_type<Comp> *>(pools)->get(rhs), ...)); +} else { +return compare(std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(lhs)...), std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(rhs)...)); +} +}; + +cpool->sort_n(*length, std::move(comp), std::move(algo), std::forward<Args>(args)...); +} + +[this](auto *head, auto *...other) { +for(auto next = *length; next; --next) { +const auto pos = next - 1; +[[maybe_unused]] const auto entt = head->data()[pos]; +(other->swap_elements(other->data()[pos], entt), ...); +} +}(std::get<storage_type<Owned> *>(pools)...); +} + +private: +const std::tuple<storage_type<Owned> *..., storage_type<Get> *...> pools; +const size_type *const length; +}; + +} // namespace entt + +#endif + +// #include "entity/handle.hpp" +#ifndef ENTT_ENTITY_HANDLE_HPP +#define ENTT_ENTITY_HANDLE_HPP + +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + +// #include "registry.hpp" +#ifndef ENTT_ENTITY_REGISTRY_HPP +#define ENTT_ENTITY_REGISTRY_HPP + +#include <algorithm> +#include <cstddef> +#include <iterator> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include <algorithm> +#include <cmath> +#include <cstddef> +#include <functional> +#include <iterator> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include <cstddef> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include <cstdint> +#include <type_traits> +// #include "../config/config.h" + + +namespace entt { + +template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)> +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type, std::size_t, typename = void> +struct compressed_pair_element { +using reference = Type &; +using const_reference = const Type &; + +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>> +compressed_pair_element() +: value{} {} + +template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>> +compressed_pair_element(Args &&args) +: value{std::forward<Args>(args)} {} + +template<typename... Args, std::size_t... Index> +compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) +: value{std::forward<Args>(std::get<Index>(args))...} {} + +[[nodiscard]] reference get() ENTT_NOEXCEPT { +return value; +} + +[[nodiscard]] const_reference get() const ENTT_NOEXCEPT { +return value; +} + +private: +Type value; +}; + +template<typename Type, std::size_t Tag> +struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type { +using reference = Type &; +using const_reference = const Type &; +using base_type = Type; + +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>> +compressed_pair_element() +: base_type{} {} + +template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>> +compressed_pair_element(Args &&args) +: base_type{std::forward<Args>(args)} {} + +template<typename... Args, std::size_t... Index> +compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) +: base_type{std::forward<Args>(std::get<Index>(args))...} {} + +[[nodiscard]] reference get() ENTT_NOEXCEPT { +return *this; +} + +[[nodiscard]] const_reference get() const ENTT_NOEXCEPT { +return *this; +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +class compressed_pair final +: internal::compressed_pair_element<First, 0u>, +internal::compressed_pair_element<Second, 1u> { +using first_base = internal::compressed_pair_element<First, 0u>; +using second_base = internal::compressed_pair_element<Second, 1u>; + +public: +/*! @brief The type of the first element that the pair stores. */ +using first_type = First; +/*! @brief The type of the second element that the pair stores. */ +using second_type = Second; + +/** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>> +constexpr compressed_pair() +: first_base{}, +second_base{} {} + +/** + * @brief Copy constructor. + * @param other The instance to copy from. + */ +constexpr compressed_pair(const compressed_pair &other) = default; + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +constexpr compressed_pair(compressed_pair &&other) = default; + +/** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ +template<typename Arg, typename Other> +constexpr compressed_pair(Arg &&arg, Other &&other) +: first_base{std::forward<Arg>(arg)}, +second_base{std::forward<Other>(other)} {} + +/** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ +template<typename... Args, typename... Other> +constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) +: first_base{std::move(args), std::index_sequence_for<Args...>{}}, +second_base{std::move(other), std::index_sequence_for<Other...>{}} {} + +/** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ +constexpr compressed_pair &operator=(const compressed_pair &other) = default; + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ +constexpr compressed_pair &operator=(compressed_pair &&other) = default; + +/** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ +[[nodiscard]] first_type &first() ENTT_NOEXCEPT { +return static_cast<first_base &>(*this).get(); +} + +/*! @copydoc first */ +[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { +return static_cast<const first_base &>(*this).get(); +} + +/** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ +[[nodiscard]] second_type &second() ENTT_NOEXCEPT { +return static_cast<second_base &>(*this).get(); +} + +/*! @copydoc second */ +[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { +return static_cast<const second_base &>(*this).get(); +} + +/** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ +void swap(compressed_pair &other) { +using std::swap; +swap(first(), other.first()); +swap(second(), other.second()); +} + +/** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ +template<std::size_t Index> +decltype(auto) get() ENTT_NOEXCEPT { +if constexpr(Index == 0u) { +return first(); +} else { +static_assert(Index == 1u, "Index out of bounds"); +return second(); +} +} + +/*! @copydoc get */ +template<std::size_t Index> +decltype(auto) get() const ENTT_NOEXCEPT { +if constexpr(Index == 0u) { +return first(); +} else { +static_assert(Index == 1u, "Index out of bounds"); +return second(); +} +} +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template<typename Type, typename Other> +compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template<typename First, typename Second> +inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) { +lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<size_t Index, typename First, typename Second> +struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> { +static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include <iterator> +#include <memory> +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template<typename Type> +struct input_iterator_pointer final { +/*! @brief Pointer type. */ +using pointer = Type *; + +/*! @brief Default copy constructor, deleted on purpose. */ +input_iterator_pointer(const input_iterator_pointer &) = delete; + +/*! @brief Default move constructor. */ +input_iterator_pointer(input_iterator_pointer &&) = default; + +/** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ +input_iterator_pointer(Type &&val) +: value{std::move(val)} {} + +/** + * @brief Default copy assignment operator, deleted on purpose. + * @return This proxy object. + */ +input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; + +/** + * @brief Default move assignment operator. + * @return This proxy object. + */ +input_iterator_pointer &operator=(input_iterator_pointer &&) = default; + +/** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ +[[nodiscard]] pointer operator->() ENTT_NOEXCEPT { +return std::addressof(value); +} + +private: +Type value; +}; + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template<typename It, typename Sentinel = It> +struct iterable_adaptor final { +/*! @brief Value type. */ +using value_type = typename std::iterator_traits<It>::value_type; +/*! @brief Iterator type. */ +using iterator = It; +/*! @brief Sentinel type. */ +using sentinel = Sentinel; + +/*! @brief Default constructor. */ +iterable_adaptor() = default; + +/** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ +iterable_adaptor(iterator from, sentinel to) +: first{from}, +last{to} {} + +/** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return first; +} + +/** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ +[[nodiscard]] sentinel end() const ENTT_NOEXCEPT { +return last; +} + +/*! @copydoc begin */ +[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { +return begin(); +} + +/*! @copydoc end */ +[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT { +return end(); +} + +private: +It first; +Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include <cstddef> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template<typename Type> +[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT { +if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) { +return ptr; +} else { +return to_address(std::forward<Type>(ptr).operator->()); +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) { +lhs = rhs; +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) { +lhs = std::move(rhs); +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); + +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) { +using std::swap; +swap(lhs, rhs); +} +} + +/** + * @brief Checks whether a value is a power of two or not. + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value. + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded"); +std::size_t curr = value - (value != 0u); + +for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) { +curr |= curr >> next; +} + +return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT { +ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); +return value & (mod - 1u); +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Args Types of arguments to use to construct the object. + */ +template<typename Allocator> +struct allocation_deleter: private Allocator { +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Pointer type. */ +using pointer = typename std::allocator_traits<Allocator>::pointer; + +/** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ +allocation_deleter(const allocator_type &alloc) +: Allocator{alloc} {} + +/** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ +void operator()(pointer ptr) { +using alloc_traits = typename std::allocator_traits<Allocator>; +alloc_traits::destroy(*this, to_address(ptr)); +alloc_traits::deallocate(*this, ptr, 1u); +} +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template<typename Type, typename Allocator, typename... Args> +auto allocate_unique(Allocator &allocator, Args &&...args) { +static_assert(!std::is_array_v<Type>, "Array types are not supported"); + +using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>; +using allocator_type = typename alloc_traits::allocator_type; + +allocator_type alloc{allocator}; +auto ptr = alloc_traits::allocate(alloc, 1u); + +ENTT_TRY { +alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...); +} +ENTT_CATCH { +alloc_traits::deallocate(alloc, ptr, 1u); +ENTT_THROW; +} + +return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc}; +} + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type> +struct uses_allocator_construction { +template<typename Allocator, typename... Params> +static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT { +if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) { +return std::forward_as_tuple(std::forward<Params>(params)...); +} else { +static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request"); + +if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) { +return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...); +} else { +static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request"); +return std::forward_as_tuple(std::forward<Params>(params)..., allocator); +} +} +} +}; + +template<typename Type, typename Other> +struct uses_allocator_construction<std::pair<Type, Other>> { +using type = std::pair<Type, Other>; + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT { +return std::make_tuple( +std::piecewise_construct, +std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)), +std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second))); +} + +template<typename Allocator> +static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second))); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT { +return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { +return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { +return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include <functional> +#include <memory> + +namespace entt { + +template< +typename Key, +typename Type, +typename = std::hash<Key>, +typename = std::equal_to<Key>, +typename = std::allocator<std::pair<const Key, Type>>> +class dense_map; + +template< +typename Type, +typename = std::hash<Type>, +typename = std::equal_to<Type>, +typename = std::allocator<Type>> +class dense_set; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Key, typename Type> +struct dense_map_node final { +using value_type = std::pair<Key, Type>; + +template<typename... Args> +dense_map_node(const std::size_t pos, Args &&...args) +: next{pos}, +element{std::forward<Args>(args)...} {} + +template<typename Allocator, typename... Args> +dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) +: next{pos}, +element{entt::make_obj_using_allocator<value_type>(allocator, std::forward<Args>(args)...)} {} + +template<typename Allocator> +dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) +: next{other.next}, +element{entt::make_obj_using_allocator<value_type>(allocator, other.element)} {} + +template<typename Allocator> +dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) +: next{other.next}, +element{entt::make_obj_using_allocator<value_type>(allocator, std::move(other.element))} {} + +std::size_t next; +value_type element; +}; + +template<typename It> +class dense_map_iterator final { +template<typename> +friend class dense_map_iterator; + +using first_type = decltype(std::as_const(std::declval<It>()->element.first)); +using second_type = decltype((std::declval<It>()->element.second)); + +public: +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; + +dense_map_iterator() ENTT_NOEXCEPT +: it{} {} + +dense_map_iterator(const It iter) ENTT_NOEXCEPT +: it{iter} {} + +template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> +dense_map_iterator(const dense_map_iterator<Other> &other) ENTT_NOEXCEPT +: it{other.it} {} + +dense_map_iterator &operator++() ENTT_NOEXCEPT { +return ++it, *this; +} + +dense_map_iterator operator++(int) ENTT_NOEXCEPT { +dense_map_iterator orig = *this; +return ++(*this), orig; +} + +dense_map_iterator &operator--() ENTT_NOEXCEPT { +return --it, *this; +} + +dense_map_iterator operator--(int) ENTT_NOEXCEPT { +dense_map_iterator orig = *this; +return operator--(), orig; +} + +dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { +it += value; +return *this; +} + +dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { +dense_map_iterator copy = *this; +return (copy += value); +} + +dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { +return (*this += -value); +} + +dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { +return (*this + -value); +} + +[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { +return {it[value].element.first, it[value].element.second}; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return {it->element.first, it->element.second}; +} + +template<typename ILhs, typename IRhs> +friend std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT; + +template<typename ILhs, typename IRhs> +friend bool operator==(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT; + +template<typename ILhs, typename IRhs> +friend bool operator<(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT; + +private: +It it; +}; + +template<typename ILhs, typename IRhs> +[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it - rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator==(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it == rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator!=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator<(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it < rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator>(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator<=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs > rhs); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator>=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +template<typename It> +class dense_map_local_iterator final { +template<typename> +friend class dense_map_local_iterator; + +using first_type = decltype(std::as_const(std::declval<It>()->element.first)); +using second_type = decltype((std::declval<It>()->element.second)); + +public: +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; + +dense_map_local_iterator() ENTT_NOEXCEPT +: it{}, +offset{} {} + +dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT +: it{iter}, +offset{pos} {} + +template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> +dense_map_local_iterator(const dense_map_local_iterator<Other> &other) ENTT_NOEXCEPT +: it{other.it}, +offset{other.offset} {} + +dense_map_local_iterator &operator++() ENTT_NOEXCEPT { +return offset = it[offset].next, *this; +} + +dense_map_local_iterator operator++(int) ENTT_NOEXCEPT { +dense_map_local_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return {it[offset].element.first, it[offset].element.second}; +} + +[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { +return offset; +} + +private: +It it; +std::size_t offset; +}; + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator==(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.index() == rhs.index(); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator!=(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Key, typename Type, typename Hash, typename KeyEqual, typename Allocator> +class dense_map { +static constexpr float default_threshold = 0.875f; +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>; +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>>; + +template<typename Other> +[[nodiscard]] std::size_t key_to_bucket(const Other &key) const ENTT_NOEXCEPT { +return fast_mod(sparse.second()(key), bucket_count()); +} + +template<typename Other> +[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { +for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { +if(packed.second()(it->first, key)) { +return begin() + static_cast<typename iterator::difference_type>(it.index()); +} +} + +return end(); +} + +template<typename Other> +[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { +for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { +if(packed.second()(it->first, key)) { +return cbegin() + static_cast<typename iterator::difference_type>(it.index()); +} +} + +return cend(); +} + +template<typename Other, typename... Args> +[[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { +const auto index = key_to_bucket(key); + +if(auto it = constrained_find(key, index); it != end()) { +return std::make_pair(it, false); +} + +packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward<Other>(key)), std::forward_as_tuple(std::forward<Args>(args)...)); +sparse.first()[index] = packed.first().size() - 1u; +rehash_if_required(); + +return std::make_pair(--end(), true); +} + +template<typename Other, typename Arg> +[[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { +const auto index = key_to_bucket(key); + +if(auto it = constrained_find(key, index); it != end()) { +it->second = std::forward<Arg>(value); +return std::make_pair(it, false); +} + +packed.first().emplace_back(sparse.first()[index], std::forward<Other>(key), std::forward<Arg>(value)); +sparse.first()[index] = packed.first().size() - 1u; +rehash_if_required(); + +return std::make_pair(--end(), true); +} + +void move_and_pop(const std::size_t pos) { +if(const auto last = size() - 1u; pos != last) { +packed.first()[pos] = std::move(packed.first().back()); +size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); +for(; *curr != last; curr = &packed.first()[*curr].next) {} +*curr = pos; +} + +packed.first().pop_back(); +} + +void rehash_if_required() { +if(size() > (bucket_count() * max_load_factor())) { +rehash(bucket_count() * 2u); +} +} + +public: +/*! @brief Key type of the container. */ +using key_type = Key; +/*! @brief Mapped type of the container. */ +using mapped_type = Type; +/*! @brief Key-value type of the container. */ +using value_type = std::pair<const Key, Type>; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Type of function to use to hash the keys. */ +using hasher = Hash; +/*! @brief Type of function to use to compare the keys for equality. */ +using key_equal = KeyEqual; +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Input iterator type. */ +using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>; +/*! @brief Constant input iterator type. */ +using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_iterator>; +/*! @brief Input iterator type. */ +using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::iterator>; +/*! @brief Constant input iterator type. */ +using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_iterator>; + +/*! @brief Default constructor. */ +dense_map() +: dense_map(minimum_capacity) {} + +/** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ +explicit dense_map(const allocator_type &allocator) +: dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param allocator The allocator to use. + */ +dense_map(const size_type bucket_count, const allocator_type &allocator) +: dense_map{bucket_count, hasher{}, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ +dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) +: dense_map{bucket_count, hash, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ +explicit dense_map(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) +: sparse{allocator, hash}, +packed{allocator, equal}, +threshold{default_threshold} { +rehash(bucket_count); +} + +/*! @brief Default copy constructor. */ +dense_map(const dense_map &) = default; + +/** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ +dense_map(const dense_map &other, const allocator_type &allocator) +: sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, +packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, +threshold{other.threshold} {} + +/*! @brief Default move constructor. */ +dense_map(dense_map &&) = default; + +/** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ +dense_map(dense_map &&other, const allocator_type &allocator) +: sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, +packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, +threshold{other.threshold} {} + +/** + * @brief Default copy assignment operator. + * @return This container. + */ +dense_map &operator=(const dense_map &) = default; + +/** + * @brief Default move assignment operator. + * @return This container. + */ +dense_map &operator=(dense_map &&) = default; + +/** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ +[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { +return sparse.first().get_allocator(); +} + +/** + * @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. + */ +[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { +return packed.first().begin(); +} + +/*! @copydoc cbegin */ +[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { +return cbegin(); +} + +/*! @copydoc begin */ +[[nodiscard]] iterator begin() ENTT_NOEXCEPT { +return packed.first().begin(); +} + +/** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the internal array. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the element following the last instance of the + * internal array. + */ +[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { +return packed.first().end(); +} + +/*! @copydoc cend */ +[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { +return cend(); +} + +/*! @copydoc end */ +[[nodiscard]] iterator end() ENTT_NOEXCEPT { +return packed.first().end(); +} + +/** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return packed.first().empty(); +} + +/** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return packed.first().size(); +} + +/*! @brief Clears the container. */ +void clear() ENTT_NOEXCEPT { +sparse.first().clear(); +packed.first().clear(); +rehash(0u); +} + +/** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +std::pair<iterator, bool> insert(const value_type &value) { +return insert_or_do_nothing(value.first, value.second); +} + +/*! @copydoc insert */ +std::pair<iterator, bool> insert(value_type &&value) { +return insert_or_do_nothing(std::move(value.first), std::move(value.second)); +} + +/** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ +template<typename Arg> +std::enable_if_t<std::is_constructible_v<value_type, Arg &&>, std::pair<iterator, bool>> +insert(Arg &&value) { +return insert_or_do_nothing(std::forward<Arg>(value).first, std::forward<Arg>(value).second); +} + +/** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ +template<typename It> +void insert(It first, It last) { +for(; first != last; ++first) { +insert(*first); +} +} + +/** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ +template<typename Arg> +std::pair<iterator, bool> insert_or_assign(const key_type &key, Arg &&value) { +return insert_or_overwrite(key, std::forward<Arg>(value)); +} + +/*! @copydoc insert_or_assign */ +template<typename Arg> +std::pair<iterator, bool> insert_or_assign(key_type &&key, Arg &&value) { +return insert_or_overwrite(std::move(key), std::forward<Arg>(value)); +} + +/** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +template<typename... Args> +std::pair<iterator, bool> emplace([[maybe_unused]] Args &&...args) { +if constexpr(sizeof...(Args) == 0u) { +return insert_or_do_nothing(key_type{}); +} else if constexpr(sizeof...(Args) == 1u) { +return insert_or_do_nothing(std::forward<Args>(args).first..., std::forward<Args>(args).second...); +} else if constexpr(sizeof...(Args) == 2u) { +return insert_or_do_nothing(std::forward<Args>(args)...); +} else { +auto &node = packed.first().emplace_back(packed.first().size(), std::forward<Args>(args)...); +const auto index = key_to_bucket(node.element.first); + +if(auto it = constrained_find(node.element.first, index); it != end()) { +packed.first().pop_back(); +return std::make_pair(it, false); +} + +std::swap(node.next, sparse.first()[index]); +rehash_if_required(); + +return std::make_pair(--end(), true); +} +} + +/** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +template<typename... Args> +std::pair<iterator, bool> try_emplace(const key_type &key, Args &&...args) { +return insert_or_do_nothing(key, std::forward<Args>(args)...); +} + +/*! @copydoc try_emplace */ +template<typename... Args> +std::pair<iterator, bool> try_emplace(key_type &&key, Args &&...args) { +return insert_or_do_nothing(std::move(key), std::forward<Args>(args)...); +} + +/** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ +iterator erase(const_iterator pos) { +const auto diff = pos - cbegin(); +erase(pos->first); +return begin() + diff; +} + +/** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ +iterator erase(const_iterator first, const_iterator last) { +const auto dist = first - cbegin(); + +for(auto from = last - cbegin(); from != dist; --from) { +erase(packed.first()[from - 1u].element.first); +} + +return (begin() + dist); +} + +/** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ +size_type erase(const key_type &key) { +for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) { +if(packed.second()(packed.first()[*curr].element.first, key)) { +const auto index = *curr; +*curr = packed.first()[*curr].next; +move_and_pop(index); +return 1u; +} +} + +return 0u; +} + +/** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ +void swap(dense_map &other) { +using std::swap; +swap(sparse, other.sparse); +swap(packed, other.packed); +swap(threshold, other.threshold); +} + +/** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ +[[nodiscard]] mapped_type &at(const key_type &key) { +auto it = find(key); +ENTT_ASSERT(it != end(), "Invalid key"); +return it->second; +} + +/*! @copydoc at */ +[[nodiscard]] const mapped_type &at(const key_type &key) const { +auto it = find(key); +ENTT_ASSERT(it != cend(), "Invalid key"); +return it->second; +} + +/** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ +[[nodiscard]] mapped_type &operator[](const key_type &key) { +return insert_or_do_nothing(key).first->second; +} + +/** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ +[[nodiscard]] mapped_type &operator[](key_type &&key) { +return insert_or_do_nothing(std::move(key)).first->second; +} + +/** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ +[[nodiscard]] iterator find(const key_type &key) { +return constrained_find(key, key_to_bucket(key)); +} + +/*! @copydoc find */ +[[nodiscard]] const_iterator find(const key_type &key) const { +return constrained_find(key, key_to_bucket(key)); +} + +/** + * @brief Finds an element with a key that compares _equivalent_ to a given + * value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>> +find(const Other &key) { +return constrained_find(key, key_to_bucket(key)); +} + +/*! @copydoc find */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>> +find(const Other &key) const { +return constrained_find(key, key_to_bucket(key)); +} + +/** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ +[[nodiscard]] bool contains(const key_type &key) const { +return (find(key) != cend()); +} + +/** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>> +contains(const Other &key) const { +return (find(key) != cend()); +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] const_local_iterator cbegin(const size_type index) const { +return {packed.first().begin(), sparse.first()[index]}; +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] const_local_iterator begin(const size_type index) const { +return cbegin(index); +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] local_iterator begin(const size_type index) { +return {packed.first().begin(), sparse.first()[index]}; +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { +return {packed.first().begin(), (std::numeric_limits<size_type>::max)()}; +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const { +return cend(index); +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { +return {packed.first().begin(), (std::numeric_limits<size_type>::max)()}; +} + +/** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ +[[nodiscard]] size_type bucket_count() const { +return sparse.first().size(); +} + +/** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ +[[nodiscard]] size_type max_bucket_count() const { +return sparse.first().max_size(); +} + +/** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ +[[nodiscard]] size_type bucket_size(const size_type index) const { +return static_cast<size_type>(std::distance(begin(index), end(index))); +} + +/** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ +[[nodiscard]] size_type bucket(const key_type &key) const { +return key_to_bucket(key); +} + +/** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ +[[nodiscard]] float load_factor() const { +return size() / static_cast<float>(bucket_count()); +} + +/** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ +[[nodiscard]] float max_load_factor() const { +return threshold; +} + +/** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ +void max_load_factor(const float value) { +ENTT_ASSERT(value > 0.f, "Invalid load factor"); +threshold = value; +rehash(0u); +} + +/** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param count New number of buckets. + */ +void rehash(const size_type count) { +auto value = (std::max)(count, minimum_capacity); +value = (std::max)(value, static_cast<size_type>(size() / max_load_factor())); + +if(const auto sz = next_power_of_two(value); sz != bucket_count()) { +sparse.first().resize(sz); +std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits<size_type>::max)()); + +for(size_type pos{}, last = size(); pos < last; ++pos) { +const auto index = key_to_bucket(packed.first()[pos].element.first); +packed.first()[pos].next = std::exchange(sparse.first()[index], pos); +} +} +} + +/** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param count New number of elements. + */ +void reserve(const size_type count) { +packed.first().reserve(count); +rehash(static_cast<size_type>(std::ceil(count / max_load_factor()))); +} + +/** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ +[[nodiscard]] hasher hash_function() const { +return sparse.second(); +} + +/** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ +[[nodiscard]] key_equal key_eq() const { +return packed.second(); +} + +private: +compressed_pair<sparse_container_type, hasher> sparse; +compressed_pair<packed_container_type, key_equal> packed; +float threshold; +}; + +} // namespace entt + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace std { + +template<typename Key, typename Value, typename Allocator> +struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator> +: std::true_type {}; + +} // namespace std + +/** + * Internal details not to be documented. + * @endcond + */ + +#endif + +// #include "../core/algorithm.hpp" + +// #include "../core/any.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { +/*! @brief Indicates that this is a transparent function object. */ +using is_transparent = void; + +/** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ +template<class Type> +[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { +return std::forward<Type>(value); +} +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template<typename Type, typename Class> +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template<typename Func> +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template<class... Func> +struct overloaded: Func... { +using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template<class... Func> +overloaded(Func...) -> overloaded<Func...>; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template<class Func> +struct y_combinator { +/** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ +y_combinator(Func recursive) +: func{std::move(recursive)} {} + +/** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ +template<class... Args> +decltype(auto) operator()(Args &&...args) const { +return func(*this, std::forward<Args>(args)...); +} + +/*! @copydoc operator()() */ +template<class... Args> +decltype(auto) operator()(Args &&...args) { +return func(*this, std::forward<Args>(args)...); +} + +private: +Func func; +}; + +} // namespace entt + +#endif + +// #include "component.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "group.hpp" +#ifndef ENTT_ENTITY_GROUP_HPP +#define ENTT_ENTITY_GROUP_HPP + +#include <tuple> +#include <type_traits> +#include <utility> +// #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" + +// #include "utility.hpp" + + +namespace entt { + +/** + * @brief Group. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template<typename, typename, typename, typename> +class basic_group; + +/** + * @brief Non-owning group. + * + * A non-owning group returns all entities and only the entities that have at + * least the given components. Moreover, it's guaranteed that the entity list + * is tightly packed in memory for fast iterations. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Groups share references to the underlying data structures of the registry + * that generated them. Therefore any change to the entities and to the + * components made by means of the registry are immediately reflected by all the + * groups.<br/> + * Moreover, sorting a non-owning group affects all the instances of the same + * group (it means that users don't have to call `sort` on each instance to sort + * all of them because they _share_ entities and components). + * + * @warning + * Lifetime of a group must not overcome that of the registry that generated it. + * In any other case, attempting to use a group results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Get Type of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + */ +template<typename Entity, typename... Get, typename... Exclude> +class basic_group<Entity, owned_t<>, get_t<Get...>, exclude_t<Exclude...>> { +/*! @brief A registry is allowed to create groups. */ +friend class basic_registry<Entity>; + +template<typename Comp> +using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>; + +using basic_common_type = std::common_type_t<typename storage_type<Get>::base_type...>; + +struct extended_group_iterator final { +using difference_type = std::ptrdiff_t; +using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({}))); +using pointer = input_iterator_pointer<value_type>; +using reference = value_type; +using iterator_category = std::input_iterator_tag; + +extended_group_iterator() = default; + +extended_group_iterator(typename basic_common_type::iterator from, const std::tuple<storage_type<Get> *...> &args) +: it{from}, +pools{args} {} + +extended_group_iterator &operator++() ENTT_NOEXCEPT { +return ++it, *this; +} + +extended_group_iterator operator++(int) ENTT_NOEXCEPT { +extended_group_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +const auto entt = *it; +return std::tuple_cat(std::make_tuple(entt), std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...); +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +[[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT { +return other.it == it; +} + +[[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT { +return !(*this == other); +} + +private: +typename basic_common_type::iterator it; +std::tuple<storage_type<Get> *...> pools; +}; + +basic_group(basic_common_type &ref, storage_type<Get> &...gpool) ENTT_NOEXCEPT +: handler{&ref}, +pools{&gpool...} {} + +public: +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Common type among all storage types. */ +using base_type = basic_common_type; +/*! @brief Random access iterator type. */ +using iterator = typename base_type::iterator; +/*! @brief Reversed iterator type. */ +using reverse_iterator = typename base_type::reverse_iterator; +/*! @brief Iterable group type. */ +using iterable = iterable_adaptor<extended_group_iterator>; + +/*! @brief Default constructor to use to create empty, invalid groups. */ +basic_group() ENTT_NOEXCEPT +: handler{} {} + +/** + * @brief Returns a const reference to the underlying handler. + * @return A const reference to the underlying handler. + */ +const base_type &handle() const ENTT_NOEXCEPT { +return *handler; +} + +/** + * @brief Returns the storage for a given component type. + * @tparam Comp Type of component of which to return the storage. + * @return The storage for the given component type. + */ +template<typename Comp> +[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { +return *std::get<storage_type<Comp> *>(pools); +} + +/** + * @brief Returns the storage for a given component type. + * @tparam Comp Index of component of which to return the storage. + * @return The storage for the given component type. + */ +template<std::size_t Comp> +[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { +return *std::get<Comp>(pools); +} + +/** + * @brief Returns the number of entities that have the given components. + * @return Number of entities that have the given components. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return *this ? handler->size() : size_type{}; +} + +/** + * @brief Returns the number of elements that a group has currently + * allocated space for. + * @return Capacity of the group. + */ +[[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { +return *this ? handler->capacity() : size_type{}; +} + +/*! @brief Requests the removal of unused capacity. */ +void shrink_to_fit() { +if(*this) { +handler->shrink_to_fit(); +} +} + +/** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return !*this || handler->empty(); +} + +/** + * @brief Returns an iterator to the first entity of the group. + * + * The returned iterator points to the first entity of the group. If the + * group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return *this ? handler->begin() : iterator{}; +} + +/** + * @brief Returns an iterator that is past the last entity of the group. + * + * The returned iterator points to the entity following the last entity of + * the group. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * group. + */ +[[nodiscard]] iterator end() const ENTT_NOEXCEPT { +return *this ? handler->end() : iterator{}; +} + +/** + * @brief Returns an iterator to the first entity of the reversed group. + * + * The returned iterator points to the first entity of the reversed group. + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ +[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { +return *this ? handler->rbegin() : reverse_iterator{}; +} + +/** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * + * The returned iterator points to the entity following the last entity of + * the reversed group. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * reversed group. + */ +[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { +return *this ? handler->rend() : reverse_iterator{}; +} + +/** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ +[[nodiscard]] entity_type front() const ENTT_NOEXCEPT { +const auto it = begin(); +return it != end() ? *it : null; +} + +/** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ +[[nodiscard]] entity_type back() const ENTT_NOEXCEPT { +const auto it = rbegin(); +return it != rend() ? *it : null; +} + +/** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ +[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT { +const auto it = *this ? handler->find(entt) : iterator{}; +return it != end() && *it == entt ? it : end(); +} + +/** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ +[[nodiscard]] entity_type operator[](const size_type pos) const { +return begin()[pos]; +} + +/** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return handler != nullptr; +} + +/** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ +[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { +return *this && handler->contains(entt); +} + +/** + * @brief Returns the components assigned to the given entity. + * + * Prefer this function instead of `registry::get` during iterations. It has + * far better performance than its counterpart. + * + * @warning + * Attempting to use an invalid component type results in a compilation + * error. Attempting to use an entity that doesn't belong to the group + * results in undefined behavior. + * + * @tparam Comp Types of components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ +template<typename... Comp> +[[nodiscard]] decltype(auto) get(const entity_type entt) const { +ENTT_ASSERT(contains(entt), "Group does not contain entity"); + +if constexpr(sizeof...(Comp) == 0) { +return std::tuple_cat(std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...); +} else if constexpr(sizeof...(Comp) == 1) { +return (std::get<storage_type<Comp> *>(pools)->get(entt), ...); +} else { +return std::tuple_cat(std::get<storage_type<Comp> *>(pools)->get_as_tuple(entt)...); +} +} + +/** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.<br/> + * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ +template<typename Func> +void each(Func func) const { +for(const auto entt: *this) { +if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) { +std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); +} else { +std::apply(func, get(entt)); +} +} +} + +/** + * @brief Returns an iterable object to use to _visit_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ +[[nodiscard]] iterable each() const ENTT_NOEXCEPT { +return handler ? iterable{extended_group_iterator{handler->begin(), pools}, extended_group_iterator{handler->end(), pools}} +: iterable{extended_group_iterator{{}, pools}, extended_group_iterator{{}, pools}}; +} + +/** + * @brief Sort a group according to the given comparison function. + * + * Sort the group so that iterating it with a couple of iterators returns + * entities and components in the expected order. See `begin` and `end` for + * more details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple<Component &...>, std::tuple<Component &...>); + * bool(const Component &..., const Component &...); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Component` are such that they are iterated by the group.<br/> + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function object must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Comp Optional types of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ +template<typename... Comp, typename Compare, typename Sort = std_sort, typename... Args> +void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { +if(*this) { +if constexpr(sizeof...(Comp) == 0) { +static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function"); +handler->sort(std::move(compare), std::move(algo), std::forward<Args>(args)...); +} else { +auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) { +if constexpr(sizeof...(Comp) == 1) { +return compare((std::get<storage_type<Comp> *>(pools)->get(lhs), ...), (std::get<storage_type<Comp> *>(pools)->get(rhs), ...)); +} else { +return compare(std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(lhs)...), std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(rhs)...)); +} +}; + +handler->sort(std::move(comp), std::move(algo), std::forward<Args>(args)...); +} +} +} + +/** + * @brief Sort the shared pool of entities according to the given component. + * + * Non-owning groups of the same type share with the registry a pool of + * entities with its own order that doesn't depend on the order of any pool + * of components. Users can order the underlying data structure so that it + * respects the order of the pool of the given component. + * + * @note + * The shared pool of entities and thus its order is affected by the changes + * to each and every pool that it tracks. Therefore changes to those pools + * can quickly ruin the order imposed to the pool of entities shared between + * the non-owning groups. + * + * @tparam Comp Type of component to use to impose the order. + */ +template<typename Comp> +void sort() const { +if(*this) { +handler->respect(*std::get<storage_type<Comp> *>(pools)); +} +} + +private: +base_type *const handler; +const std::tuple<storage_type<Get> *...> pools; +}; + +/** + * @brief Owning group. + * + * Owning groups return all entities and only the entities that have at least + * the given components. Moreover: + * + * * It's guaranteed that the entity list is tightly packed in memory for fast + * iterations. + * * It's guaranteed that the lists of owned components are tightly packed in + * memory for even faster iterations and to allow direct access. + * * They stay true to the order of the owned components and all instances have + * the same order in memory. + * + * The more types of components are owned by a group, the faster it is to + * iterate them. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Groups share references to the underlying data structures of the registry + * that generated them. Therefore any change to the entities and to the + * components made by means of the registry are immediately reflected by all the + * groups. + * Moreover, sorting an owning group affects all the instance of the same group + * (it means that users don't have to call `sort` on each instance to sort all + * of them because they share the underlying data structure). + * + * @warning + * Lifetime of a group must not overcome that of the registry that generated it. + * In any other case, attempting to use a group results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Owned Types of components owned by the group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + */ +template<typename Entity, typename... Owned, typename... Get, typename... Exclude> +class basic_group<Entity, owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> { +/*! @brief A registry is allowed to create groups. */ +friend class basic_registry<Entity>; + +template<typename Comp> +using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>; + +using basic_common_type = std::common_type_t<typename storage_type<Owned>::base_type..., typename storage_type<Get>::base_type...>; + +class extended_group_iterator final { +template<typename Type> +auto index_to_element(storage_type<Type> &cpool) const { +if constexpr(ignore_as_empty_v<std::remove_const_t<Type>>) { +return std::make_tuple(); +} else { +return std::forward_as_tuple(cpool.rbegin()[it.index()]); +} +} + +public: +using difference_type = std::ptrdiff_t; +using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({}))); +using pointer = input_iterator_pointer<value_type>; +using reference = value_type; +using iterator_category = std::input_iterator_tag; + +extended_group_iterator() = default; + +template<typename... Other> +extended_group_iterator(typename basic_common_type::iterator from, const std::tuple<storage_type<Owned> *..., storage_type<Get> *...> &cpools) +: it{from}, +pools{cpools} {} + +extended_group_iterator &operator++() ENTT_NOEXCEPT { +return ++it, *this; +} + +extended_group_iterator operator++(int) ENTT_NOEXCEPT { +extended_group_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return std::tuple_cat( +std::make_tuple(*it), +index_to_element<Owned>(*std::get<storage_type<Owned> *>(pools))..., +std::get<storage_type<Get> *>(pools)->get_as_tuple(*it)...); +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +[[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT { +return other.it == it; +} + +[[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT { +return !(*this == other); +} + +private: +typename basic_common_type::iterator it; +std::tuple<storage_type<Owned> *..., storage_type<Get> *...> pools; +}; + +basic_group(const std::size_t &extent, storage_type<Owned> &...opool, storage_type<Get> &...gpool) ENTT_NOEXCEPT +: pools{&opool..., &gpool...}, +length{&extent} {} + +public: +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Common type among all storage types. */ +using base_type = basic_common_type; +/*! @brief Random access iterator type. */ +using iterator = typename base_type::iterator; +/*! @brief Reversed iterator type. */ +using reverse_iterator = typename base_type::reverse_iterator; +/*! @brief Iterable group type. */ +using iterable = iterable_adaptor<extended_group_iterator>; + +/*! @brief Default constructor to use to create empty, invalid groups. */ +basic_group() ENTT_NOEXCEPT +: length{} {} + +/** + * @brief Returns the storage for a given component type. + * @tparam Comp Type of component of which to return the storage. + * @return The storage for the given component type. + */ +template<typename Comp> +[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { +return *std::get<storage_type<Comp> *>(pools); +} + +/** + * @brief Returns the storage for a given component type. + * @tparam Comp Index of component of which to return the storage. + * @return The storage for the given component type. + */ +template<std::size_t Comp> +[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { +return *std::get<Comp>(pools); +} + +/** + * @brief Returns the number of entities that have the given components. + * @return Number of entities that have the given components. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return *this ? *length : size_type{}; +} + +/** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return !*this || !*length; +} + +/** + * @brief Returns an iterator to the first entity of the group. + * + * The returned iterator points to the first entity of the group. If the + * group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return *this ? (std::get<0>(pools)->base_type::end() - *length) : iterator{}; +} + +/** + * @brief Returns an iterator that is past the last entity of the group. + * + * The returned iterator points to the entity following the last entity of + * the group. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * group. + */ +[[nodiscard]] iterator end() const ENTT_NOEXCEPT { +return *this ? std::get<0>(pools)->base_type::end() : iterator{}; +} + +/** + * @brief Returns an iterator to the first entity of the reversed group. + * + * The returned iterator points to the first entity of the reversed group. + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ +[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { +return *this ? std::get<0>(pools)->base_type::rbegin() : reverse_iterator{}; +} + +/** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * + * The returned iterator points to the entity following the last entity of + * the reversed group. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * reversed group. + */ +[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { +return *this ? (std::get<0>(pools)->base_type::rbegin() + *length) : reverse_iterator{}; +} + +/** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ +[[nodiscard]] entity_type front() const ENTT_NOEXCEPT { +const auto it = begin(); +return it != end() ? *it : null; +} + +/** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ +[[nodiscard]] entity_type back() const ENTT_NOEXCEPT { +const auto it = rbegin(); +return it != rend() ? *it : null; +} + +/** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ +[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT { +const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{}; +return it != end() && it >= begin() && *it == entt ? it : end(); +} + +/** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ +[[nodiscard]] entity_type operator[](const size_type pos) const { +return begin()[pos]; +} + +/** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return length != nullptr; +} + +/** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ +[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { +return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length)); +} + +/** + * @brief Returns the components assigned to the given entity. + * + * Prefer this function instead of `registry::get` during iterations. It has + * far better performance than its counterpart. + * + * @warning + * Attempting to use an invalid component type results in a compilation + * error. Attempting to use an entity that doesn't belong to the group + * results in undefined behavior. + * + * @tparam Comp Types of components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ +template<typename... Comp> +[[nodiscard]] decltype(auto) get(const entity_type entt) const { +ENTT_ASSERT(contains(entt), "Group does not contain entity"); + +if constexpr(sizeof...(Comp) == 0) { +return std::tuple_cat(std::get<storage_type<Owned> *>(pools)->get_as_tuple(entt)..., std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...); +} else if constexpr(sizeof...(Comp) == 1) { +return (std::get<storage_type<Comp> *>(pools)->get(entt), ...); +} else { +return std::tuple_cat(std::get<storage_type<Comp> *>(pools)->get_as_tuple(entt)...); +} +} + +/** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.<br/> + * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ +template<typename Func> +void each(Func func) const { +for(auto args: each()) { +if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) { +std::apply(func, args); +} else { +std::apply([&func](auto, auto &&...less) { func(std::forward<decltype(less)>(less)...); }, args); +} +} +} + +/** + * @brief Returns an iterable object to use to _visit_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ +[[nodiscard]] iterable each() const ENTT_NOEXCEPT { +iterator last = length ? std::get<0>(pools)->basic_common_type::end() : iterator{}; +return {extended_group_iterator{last - *length, pools}, extended_group_iterator{last, pools}}; +} + +/** + * @brief Sort a group according to the given comparison function. + * + * Sort the group so that iterating it with a couple of iterators returns + * entities and components in the expected order. See `begin` and `end` for + * more details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple<Component &...>, std::tuple<Component &...>); + * bool(const Component &, const Component &); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Component` are either owned types or not but still such that they + * are iterated by the group.<br/> + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function object must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Comp Optional types of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ +template<typename... Comp, typename Compare, typename Sort = std_sort, typename... Args> +void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const { +auto *cpool = std::get<0>(pools); + +if constexpr(sizeof...(Comp) == 0) { +static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function"); +cpool->sort_n(*length, std::move(compare), std::move(algo), std::forward<Args>(args)...); +} else { +auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) { +if constexpr(sizeof...(Comp) == 1) { +return compare((std::get<storage_type<Comp> *>(pools)->get(lhs), ...), (std::get<storage_type<Comp> *>(pools)->get(rhs), ...)); +} else { +return compare(std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(lhs)...), std::forward_as_tuple(std::get<storage_type<Comp> *>(pools)->get(rhs)...)); +} +}; + +cpool->sort_n(*length, std::move(comp), std::move(algo), std::forward<Args>(args)...); +} + +[this](auto *head, auto *...other) { +for(auto next = *length; next; --next) { +const auto pos = next - 1; +[[maybe_unused]] const auto entt = head->data()[pos]; +(other->swap_elements(other->data()[pos], entt), ...); +} +}(std::get<storage_type<Owned> *>(pools)...); +} + +private: +const std::tuple<storage_type<Owned> *..., storage_type<Get> *...> pools; +const size_type *const length; +}; + +} // namespace entt + +#endif + +// #include "runtime_view.hpp" +#ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP +#define ENTT_ENTITY_RUNTIME_VIEW_HPP + +#include <algorithm> +#include <iterator> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Set> +class runtime_view_iterator final { +using iterator_type = typename Set::iterator; + +[[nodiscard]] bool valid() const { +return (!tombstone_check || *it != tombstone) +&& std::all_of(++pools->begin(), pools->end(), [entt = *it](const auto *curr) { return curr->contains(entt); }) +&& std::none_of(filter->cbegin(), filter->cend(), [entt = *it](const auto *curr) { return curr && curr->contains(entt); }); +} + +public: +using difference_type = typename iterator_type::difference_type; +using value_type = typename iterator_type::value_type; +using pointer = typename iterator_type::pointer; +using reference = typename iterator_type::reference; +using iterator_category = std::bidirectional_iterator_tag; + +runtime_view_iterator() ENTT_NOEXCEPT +: pools{}, +filter{}, +it{}, +tombstone_check{} {} + +runtime_view_iterator(const std::vector<const Set *> &cpools, const std::vector<const Set *> &ignore, iterator_type curr) ENTT_NOEXCEPT +: pools{&cpools}, +filter{&ignore}, +it{curr}, +tombstone_check{pools->size() == 1u && (*pools)[0u]->policy() == deletion_policy::in_place} { +if(it != (*pools)[0]->end() && !valid()) { +++(*this); +} +} + +runtime_view_iterator &operator++() { +while(++it != (*pools)[0]->end() && !valid()) {} +return *this; +} + +runtime_view_iterator operator++(int) { +runtime_view_iterator orig = *this; +return ++(*this), orig; +} + +runtime_view_iterator &operator--() { +while(--it != (*pools)[0]->begin() && !valid()) {} +return *this; +} + +runtime_view_iterator operator--(int) { +runtime_view_iterator orig = *this; +return operator--(), orig; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return it.operator->(); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return *operator->(); +} + +[[nodiscard]] bool operator==(const runtime_view_iterator &other) const ENTT_NOEXCEPT { +return it == other.it; +} + +[[nodiscard]] bool operator!=(const runtime_view_iterator &other) const ENTT_NOEXCEPT { +return !(*this == other); +} + +private: +const std::vector<const Set *> *pools; +const std::vector<const Set *> *filter; +iterator_type it; +bool tombstone_check; +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Runtime view implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template<typename> +struct basic_runtime_view; + +/** + * @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. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all the other cases, modifying the pools of the given components in any + * way invalidates all the iterators and using them results in undefined + * behavior. + * + * @note + * Views share references to the underlying data structures of the registry that + * generated them. Therefore any change to the entities and to the components + * made by means of the registry are immediately reflected by the views, unless + * a pool was missing when the view was built (in this case, the view won't + * have a valid reference and won't be updated accordingly). + * + * @warning + * Lifetime of a view must not overcome that of the registry that generated it. + * In any other case, attempting to use a view results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Entity, typename Allocator> +struct basic_runtime_view<basic_sparse_set<Entity, Allocator>> { +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Common type among all storage types. */ +using base_type = basic_sparse_set<Entity, Allocator>; +/*! @brief Bidirectional iterator type. */ +using iterator = internal::runtime_view_iterator<base_type>; + +/*! @brief Default constructor to use to create empty, invalid views. */ +basic_runtime_view() ENTT_NOEXCEPT +: pools{}, +filter{} {} + +/** + * @brief Appends an opaque storage object to a runtime view. + * @param base An opaque reference to a storage object. + * @return This runtime view. + */ +basic_runtime_view &iterate(const base_type &base) { +if(pools.empty() || !(base.size() < pools[0u]->size())) { +pools.push_back(&base); +} else { +pools.push_back(std::exchange(pools[0u], &base)); +} + +return *this; +} + +/** + * @brief Adds an opaque storage object as a filter of a runtime view. + * @param base An opaque reference to a storage object. + * @return This runtime view. + */ +basic_runtime_view &exclude(const base_type &base) { +filter.push_back(&base); +return *this; +} + +/** + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. + */ +[[nodiscard]] size_type size_hint() const { +return pools.empty() ? size_type{} : pools.front()->size(); +} + +/** + * @brief Returns an iterator to the first entity that has the given + * components. + * + * The returned iterator points to the first entity that has the given + * components. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @return An iterator to the first entity that has the given components. + */ +[[nodiscard]] iterator begin() const { +return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->begin()}; +} + +/** + * @brief Returns an iterator that is past the last entity that has the + * given components. + * + * The returned iterator points to the entity following the last entity that + * has the given components. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the entity following the last entity that has the + * given components. + */ +[[nodiscard]] iterator end() const { +return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()}; +} + +/** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ +[[nodiscard]] bool contains(const entity_type entt) const { +return !pools.empty() +&& std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); }) +&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); }); +} + +/** + * @brief Iterates entities and applies the given function object to them. + * + * The function object is invoked for each entity. It is provided only with + * the entity itself. To get the components, users can use the registry with + * which the view was built.<br/> + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const entity_type); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ +template<typename Func> +void each(Func func) const { +for(const auto entity: *this) { +func(entity); +} +} + +private: +std::vector<const base_type *> pools; +std::vector<const base_type *> filter; +}; + +} // namespace entt + +#endif + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + +// #include "utility.hpp" + +// #include "view.hpp" +#ifndef ENTT_ENTITY_VIEW_HPP +#define ENTT_ENTITY_VIEW_HPP + +#include <algorithm> +#include <array> +#include <iterator> +#include <tuple> +#include <type_traits> +#include <utility> +// #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" + +// #include "utility.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type, std::size_t Component, std::size_t Exclude> +class view_iterator final { +using iterator_type = typename Type::const_iterator; + +[[nodiscard]] bool valid() const ENTT_NOEXCEPT { +return ((Component != 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); +} + +public: +using value_type = typename iterator_type::value_type; +using pointer = typename iterator_type::pointer; +using reference = typename iterator_type::reference; +using difference_type = typename iterator_type::difference_type; +using iterator_category = std::forward_iterator_tag; + +view_iterator() ENTT_NOEXCEPT = default; + +view_iterator(iterator_type curr, iterator_type to, std::array<const Type *, Component> all_of, std::array<const Type *, Exclude> none_of) ENTT_NOEXCEPT +: it{curr}, +last{to}, +pools{all_of}, +filter{none_of} { +if(it != last && !valid()) { +++(*this); +} +} + +view_iterator &operator++() ENTT_NOEXCEPT { +while(++it != last && !valid()) {} +return *this; +} + +view_iterator operator++(int) ENTT_NOEXCEPT { +view_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return &*it; +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return *operator->(); +} + +template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs> +friend bool operator==(const view_iterator<LhsType, LhsArgs...> &, const view_iterator<RhsType, RhsArgs...> &) ENTT_NOEXCEPT; + +private: +iterator_type it; +iterator_type last; +std::array<const Type *, Component> pools; +std::array<const Type *, Exclude> filter; +}; + +template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs> +[[nodiscard]] bool operator==(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) ENTT_NOEXCEPT { +return lhs.it == rhs.it; +} + +template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs> +[[nodiscard]] bool operator!=(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +template<typename It, typename... Storage> +struct extended_view_iterator final { +using difference_type = std::ptrdiff_t; +using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::declval<Storage>().get_as_tuple({})...)); +using pointer = input_iterator_pointer<value_type>; +using reference = value_type; +using iterator_category = std::input_iterator_tag; + +extended_view_iterator() = default; + +extended_view_iterator(It from, std::tuple<Storage *...> storage) +: it{from}, +pools{storage} {} + +extended_view_iterator &operator++() ENTT_NOEXCEPT { +return ++it, *this; +} + +extended_view_iterator operator++(int) ENTT_NOEXCEPT { +extended_view_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return std::apply([entt = *it](auto *...curr) { return std::tuple_cat(std::make_tuple(entt), curr->get_as_tuple(entt)...); }, pools); +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +template<typename... Lhs, typename... Rhs> +friend bool operator==(const extended_view_iterator<Lhs...> &, const extended_view_iterator<Rhs...> &) ENTT_NOEXCEPT; + +private: +It it; +std::tuple<Storage *...> pools; +}; + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] bool operator==(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) ENTT_NOEXCEPT { +return lhs.it == rhs.it; +} + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] bool operator!=(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief View implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template<typename, typename, typename, typename> +class basic_view; + +/** + * @brief Multi component view. + * + * Multi component views iterate over those entities that have at least all the + * given components in their bags. During initialization, a multi component view + * looks at the number of entities available for each component and uses the + * smallest set in order to get a performance boost when iterate. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the view in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Types of components iterated by the view. + * @tparam Exclude Types of components used to filter the view. + */ +template<typename Entity, typename... Component, typename... Exclude> +class basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>> { +template<typename, typename, typename, typename> +friend class basic_view; + +template<typename Comp> +using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>; + +template<std::size_t... Index> +[[nodiscard]] auto pools_to_array(std::index_sequence<Index...>) const ENTT_NOEXCEPT { +std::size_t pos{}; +std::array<const base_type *, sizeof...(Component) - 1u> other{}; +(static_cast<void>(std::get<Index>(pools) == view ? void() : void(other[pos++] = std::get<Index>(pools))), ...); +return other; +} + +template<std::size_t Comp, std::size_t Other, typename... Args> +[[nodiscard]] auto dispatch_get(const std::tuple<Entity, Args...> &curr) const { +if constexpr(Comp == Other) { +return std::forward_as_tuple(std::get<Args>(curr)...); +} else { +return std::get<Other>(pools)->get_as_tuple(std::get<0>(curr)); +} +} + +template<std::size_t Comp, typename Func, std::size_t... Index> +void each(Func func, std::index_sequence<Index...>) const { +for(const auto curr: std::get<Comp>(pools)->each()) { +const auto entt = std::get<0>(curr); + +if(((sizeof...(Component) != 1u) || (entt != tombstone)) +&& ((Comp == Index || std::get<Index>(pools)->contains(entt)) && ...) +&& std::apply([entt](const auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { +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<Comp, Index>(curr)...)); +} else { +std::apply(func, std::tuple_cat(dispatch_get<Comp, Index>(curr)...)); +} +} +} +} + +template<typename Func, std::size_t... Index> +void pick_and_each(Func func, std::index_sequence<Index...> seq) const { +((std::get<Index>(pools) == view ? each<Index>(std::move(func), seq) : void()), ...); +} + +public: +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Common type among all storage types. */ +using base_type = std::common_type_t<typename storage_type<Component>::base_type...>; +/*! @brief Bidirectional iterator type. */ +using iterator = internal::view_iterator<base_type, sizeof...(Component) - 1u, sizeof...(Exclude)>; +/*! @brief Iterable view type. */ +using iterable = iterable_adaptor<internal::extended_view_iterator<iterator, storage_type<Component>...>>; + +/*! @brief Default constructor to use to create empty, invalid views. */ +basic_view() ENTT_NOEXCEPT +: pools{}, +filter{}, +view{} {} + +/** + * @brief Constructs a multi-type view from a set of storage classes. + * @param component The storage for the types to iterate. + * @param epool The storage for the types used to filter the view. + */ +basic_view(storage_type<Component> &...component, const storage_type<Exclude> &...epool) ENTT_NOEXCEPT +: pools{&component...}, +filter{&epool...}, +view{(std::min)({&static_cast<const base_type &>(component)...}, [](auto *lhs, auto *rhs) { return lhs->size() < rhs->size(); })} {} + +/** + * @brief Creates a new view driven by a given component in its iterations. + * @tparam Comp Type of component used to drive the iteration. + * @return A new view driven by the given component in its iterations. + */ +template<typename Comp> +[[nodiscard]] basic_view use() const ENTT_NOEXCEPT { +basic_view other{*this}; +other.view = std::get<storage_type<Comp> *>(pools); +return other; +} + +/** + * @brief Creates a new view driven by a given component in its iterations. + * @tparam Comp Index of the component used to drive the iteration. + * @return A new view driven by the given component in its iterations. + */ +template<std::size_t Comp> +[[nodiscard]] basic_view use() const ENTT_NOEXCEPT { +basic_view other{*this}; +other.view = std::get<Comp>(pools); +return other; +} + +/** + * @brief Returns the leading storage of a view. + * @return The leading storage of the view. + */ +const base_type &handle() const ENTT_NOEXCEPT { +return *view; +} + +/** + * @brief Returns the storage for a given component type. + * @tparam Comp Type of component of which to return the storage. + * @return The storage for the given component type. + */ +template<typename Comp> +[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { +return *std::get<storage_type<Comp> *>(pools); +} + +/** + * @brief Returns the storage for a given component type. + * @tparam Comp Index of component of which to return the storage. + * @return The storage for the given component type. + */ +template<std::size_t Comp> +[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { +return *std::get<Comp>(pools); +} + +/** + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. + */ +[[nodiscard]] size_type size_hint() const ENTT_NOEXCEPT { +return view->size(); +} + +/** + * @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()`. + * + * @return An iterator to the first entity of the view. + */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return iterator{view->begin(), view->end(), pools_to_array(std::index_sequence_for<Component...>{}), filter}; +} + +/** + * @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 ENTT_NOEXCEPT { +return iterator{view->end(), view->end(), pools_to_array(std::index_sequence_for<Component...>{}), filter}; +} + +/** + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity + * otherwise. + */ +[[nodiscard]] entity_type front() const ENTT_NOEXCEPT { +const auto it = begin(); +return it != end() ? *it : null; +} + +/** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ +[[nodiscard]] entity_type back() const ENTT_NOEXCEPT { +auto it = view->rbegin(); +for(const auto last = view->rend(); it != last && !contains(*it); ++it) {} +return it == view->rend() ? null : *it; +} + +/** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ +[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT { +return contains(entt) ? iterator{view->find(entt), view->end(), pools_to_array(std::index_sequence_for<Component...>{}), filter} : end(); +} + +/** + * @brief Returns the components assigned to the given entity. + * @param entt A valid identifier. + * @return The components assigned to the given entity. + */ +[[nodiscard]] decltype(auto) operator[](const entity_type entt) const { +return get<Component...>(entt); +} + +/** + * @brief Checks if a view is properly initialized. + * @return True if the view is properly initialized, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return view != nullptr; +} + +/** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ +[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { +return std::apply([entt](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) +&& std::apply([entt](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter); +} + +/** + * @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 Comp Types of components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ +template<typename... Comp> +[[nodiscard]] decltype(auto) get(const entity_type entt) const { +ENTT_ASSERT(contains(entt), "View does not contain entity"); + +if constexpr(sizeof...(Comp) == 0) { +return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); +} else if constexpr(sizeof...(Comp) == 1) { +return (std::get<storage_type<Comp> *>(pools)->get(entt), ...); +} else { +return std::tuple_cat(std::get<storage_type<Comp> *>(pools)->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 { +ENTT_ASSERT(contains(entt), "View does not contain entity"); + +if constexpr(sizeof...(Other) == 0) { +return std::get<First>(pools)->get(entt); +} else { +return std::tuple_cat(std::get<First>(pools)->get_as_tuple(entt), std::get<Other>(pools)->get_as_tuple(entt)...); +} +} + +/** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.<br/> + * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ +template<typename Func> +void each(Func func) const { +pick_and_each(std::move(func), std::index_sequence_for<Component...>{}); +} + +/** + * @brief Returns an iterable object to use to _visit_ a view. + * + * The iterable object returns a tuple that contains the current entity and + * a set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @return An iterable object to use to _visit_ the view. + */ +[[nodiscard]] iterable each() const ENTT_NOEXCEPT { +return {internal::extended_view_iterator{begin(), pools}, internal::extended_view_iterator{end(), pools}}; +} + +/** + * @brief Combines two views in a _more specific_ one (friend function). + * @tparam Get Component list of the view to combine with. + * @tparam Excl Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ +template<typename... Get, typename... Excl> +[[nodiscard]] auto operator|(const basic_view<Entity, get_t<Get...>, exclude_t<Excl...>> &other) const ENTT_NOEXCEPT { +using view_type = basic_view<Entity, get_t<Component..., Get...>, exclude_t<Exclude..., Excl...>>; +return std::make_from_tuple<view_type>(std::tuple_cat( +std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, pools), +std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, other.pools), +std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast<const storage_type<Exclude> &>(*curr)...); }, filter), +std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast<const storage_type<Excl> &>(*curr)...); }, other.filter))); +} + +private: +std::tuple<storage_type<Component> *...> pools; +std::array<const base_type *, sizeof...(Exclude)> filter; +const base_type *view; +}; + +/** + * @brief Single component view specialization. + * + * Single component views are specialized in order to get a boost in terms of + * performance. This kind of views can access the underlying data structure + * directly and avoid superfluous checks. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given component are created and assigned to entities. + * * The entity currently pointed is modified (as an example, the given + * component is removed from the entity to which the iterator points). + * * 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. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Type of component iterated by the view. + */ +template<typename Entity, typename Component> +class basic_view<Entity, get_t<Component>, exclude_t<>, std::void_t<std::enable_if_t<!component_traits<std::remove_const_t<Component>>::in_place_delete>>> { +template<typename, typename, typename, typename> +friend class basic_view; + +using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Component>>::storage_type, Component>; + +public: +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Common type among all storage types. */ +using base_type = typename storage_type::base_type; +/*! @brief Random access iterator type. */ +using iterator = typename base_type::iterator; +/*! @brief Reversed iterator type. */ +using reverse_iterator = typename base_type::reverse_iterator; +/*! @brief Iterable view type. */ +using iterable = decltype(std::declval<storage_type>().each()); + +/*! @brief Default constructor to use to create empty, invalid views. */ +basic_view() ENTT_NOEXCEPT +: pools{}, +filter{}, +view{} {} + +/** + * @brief Constructs a single-type view from a storage class. + * @param ref The storage for the type to iterate. + */ +basic_view(storage_type &ref) ENTT_NOEXCEPT +: pools{&ref}, +filter{}, +view{&ref} {} + +/** + * @brief Returns the leading storage of a view. + * @return The leading storage of the view. + */ +const base_type &handle() const ENTT_NOEXCEPT { +return *view; +} + +/** + * @brief Returns the storage for a given component type. + * @tparam Comp Type of component of which to return the storage. + * @return The storage for the given component type. + */ +template<typename Comp = Component> +[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { +static_assert(std::is_same_v<Comp, Component>, "Invalid component type"); +return *std::get<0>(pools); +} + +/** + * @brief Returns the storage for a given component type. + * @tparam Comp Index of component of which to return the storage. + * @return The storage for the given component type. + */ +template<std::size_t Comp> +[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { +return *std::get<Comp>(pools); +} + +/** + * @brief Returns the number of entities that have the given component. + * @return Number of entities that have the given component. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return view->size(); +} + +/** + * @brief Checks whether a view is empty. + * @return True if the view is empty, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return 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()`. + * + * @return An iterator to the first entity of the view. + */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return view->begin(); +} + +/** + * @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 ENTT_NOEXCEPT { +return view->end(); +} + +/** + * @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()`. + * + * @return An iterator to the first entity of the reversed view. + */ +[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { +return view->rbegin(); +} + +/** + * @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 ENTT_NOEXCEPT { +return view->rend(); +} + +/** + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity + * otherwise. + */ +[[nodiscard]] entity_type front() const ENTT_NOEXCEPT { +return empty() ? null : *begin(); +} + +/** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ +[[nodiscard]] entity_type back() const ENTT_NOEXCEPT { +return empty() ? null : *rbegin(); +} + +/** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ +[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT { +return contains(entt) ? view->find(entt) : end(); +} + +/** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ +[[nodiscard]] entity_type operator[](const size_type pos) const { +return begin()[pos]; +} + +/** + * @brief Returns the component assigned to the given entity. + * @param entt A valid identifier. + * @return The component assigned to the given entity. + */ +[[nodiscard]] decltype(auto) operator[](const entity_type entt) const { +return get<Component>(entt); +} + +/** + * @brief Checks if a view is properly initialized. + * @return True if the view is properly initialized, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return view != nullptr; +} + +/** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ +[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { +return view->contains(entt); +} + +/** + * @brief Returns the component assigned to the given entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the view results in + * undefined behavior. + * + * @tparam Comp Type or index of the component to get. + * @param entt A valid identifier. + * @return The component assigned to the entity. + */ +template<typename... Comp> +[[nodiscard]] decltype(auto) get(const entity_type entt) const { +ENTT_ASSERT(contains(entt), "View does not contain entity"); + +if constexpr(sizeof...(Comp) == 0) { +return std::get<0>(pools)->get_as_tuple(entt); +} else { +static_assert(std::is_same_v<Comp..., Component>, "Invalid component type"); +return std::get<0>(pools)->get(entt); +} +} + +/*! @copydoc get */ +template<std::size_t Comp> +[[nodiscard]] decltype(auto) get(const entity_type entt) const { +ENTT_ASSERT(contains(entt), "View does not contain entity"); +return std::get<0>(pools)->get(entt); +} + +/** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a reference to the component if it's a non-empty one. + * The _constness_ of the component is as requested.<br/> + * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Component &); + * void(Component &); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ +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(std::is_invocable_v<Func, Component &>) { +for(auto &&component: *std::get<0>(pools)) { +func(component); +} +} else if constexpr(std::is_invocable_v<Func, Entity>) { +for(auto entity: *view) { +func(entity); +} +} else { +for(size_type pos{}, last = size(); pos < last; ++pos) { +func(); +} +} +} + +/** + * @brief Returns an iterable object to use to _visit_ a view. + * + * The iterable object returns a tuple that contains the current entity and + * a reference to its component if it's a non-empty one. The _constness_ of + * the component is as requested. + * + * @return An iterable object to use to _visit_ the view. + */ +[[nodiscard]] iterable each() const ENTT_NOEXCEPT { +return std::get<0>(pools)->each(); +} + +/** + * @brief Combines two views in a _more specific_ one (friend function). + * @tparam Get Component list of the view to combine with. + * @tparam Excl Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ +template<typename... Get, typename... Excl> +[[nodiscard]] auto operator|(const basic_view<Entity, get_t<Get...>, exclude_t<Excl...>> &other) const ENTT_NOEXCEPT { +using view_type = basic_view<Entity, get_t<Component, Get...>, exclude_t<Excl...>>; +return std::make_from_tuple<view_type>(std::tuple_cat( +std::forward_as_tuple(*std::get<0>(pools)), +std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, other.pools), +std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast<const typename view_type::template storage_type<Excl> &>(*curr)...); }, other.filter))); +} + +private: +std::tuple<storage_type *> pools; +std::array<const base_type *, 0u> filter; +const base_type *view; +}; + +/** + * @brief Deduction guide. + * @tparam Storage Type of storage classes used to create the view. + * @param storage The storage for the types to iterate. + */ +template<typename... Storage> +basic_view(Storage &...storage) -> basic_view<std::common_type_t<typename Storage::entity_type...>, get_t<constness_as_t<typename Storage::value_type, Storage>...>, exclude_t<>>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename It> +class storage_proxy_iterator final { +template<typename Other> +friend class storage_proxy_iterator; + +using mapped_type = std::remove_reference_t<decltype(std::declval<It>()->second)>; + +public: +using value_type = std::pair<id_type, constness_as_t<typename mapped_type::element_type, mapped_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; + +storage_proxy_iterator() ENTT_NOEXCEPT +: it{} {} + +storage_proxy_iterator(const It iter) ENTT_NOEXCEPT +: it{iter} {} + +template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> +storage_proxy_iterator(const storage_proxy_iterator<Other> &other) ENTT_NOEXCEPT +: it{other.it} {} + +storage_proxy_iterator &operator++() ENTT_NOEXCEPT { +return ++it, *this; +} + +storage_proxy_iterator operator++(int) ENTT_NOEXCEPT { +storage_proxy_iterator orig = *this; +return ++(*this), orig; +} + +storage_proxy_iterator &operator--() ENTT_NOEXCEPT { +return --it, *this; +} + +storage_proxy_iterator operator--(int) ENTT_NOEXCEPT { +storage_proxy_iterator orig = *this; +return operator--(), orig; +} + +storage_proxy_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { +it += value; +return *this; +} + +storage_proxy_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { +storage_proxy_iterator copy = *this; +return (copy += value); +} + +storage_proxy_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { +return (*this += -value); +} + +storage_proxy_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { +return (*this + -value); +} + +[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { +return {it[value].first, *it[value].second}; +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return {it->first, *it->second}; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +template<typename ILhs, typename IRhs> +friend std::ptrdiff_t operator-(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT; + +template<typename ILhs, typename IRhs> +friend bool operator==(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT; + +template<typename ILhs, typename IRhs> +friend bool operator<(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT; + +private: +It it; +}; + +template<typename ILhs, typename IRhs> +[[nodiscard]] std::ptrdiff_t operator-(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it - rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator==(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it == rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator!=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator<(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it < rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator>(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator<=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs > rhs); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator>=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +struct registry_context { +template<typename Type, typename... Args> +Type &emplace_hint(const id_type id, Args &&...args) { +return any_cast<Type &>(data.try_emplace(id, std::in_place_type<Type>, std::forward<Args>(args)...).first->second); +} + +template<typename Type, typename... Args> +Type &emplace(Args &&...args) { +return emplace_hint<Type>(type_id<Type>().hash(), std::forward<Args>(args)...); +} + +template<typename Type> +bool erase(const id_type id = type_id<Type>().hash()) { +const auto it = data.find(id); +return it != data.end() && it->second.type() == type_id<Type>() ? (data.erase(it), true) : false; +} + +template<typename Type> +[[nodiscard]] std::add_const_t<Type> &at(const id_type id = type_id<Type>().hash()) const { +return any_cast<std::add_const_t<Type> &>(data.at(id)); +} + +template<typename Type> +[[nodiscard]] Type &at(const id_type id = type_id<Type>().hash()) { +return any_cast<Type &>(data.at(id)); +} + +template<typename Type> +[[nodiscard]] std::add_const_t<Type> *find(const id_type id = type_id<Type>().hash()) const { +const auto it = data.find(id); +return it != data.cend() ? any_cast<std::add_const_t<Type>>(&it->second) : nullptr; +} + +template<typename Type> +[[nodiscard]] Type *find(const id_type id = type_id<Type>().hash()) { +const auto it = data.find(id); +return it != data.end() ? any_cast<Type>(&it->second) : nullptr; +} + +template<typename Type> +[[nodiscard]] bool contains(const id_type id = type_id<Type>().hash()) const { +const auto it = data.find(id); +return it != data.end() && it->second.type() == type_id<Type>(); +} + +private: +dense_map<id_type, basic_any<0u>, identity> data; +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Fast and reliable entity-component system. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +class basic_registry { +using entity_traits = entt_traits<Entity>; +using basic_common_type = basic_sparse_set<Entity>; + +template<typename Component> +using storage_type = typename storage_traits<Entity, Component>::storage_type; + +template<typename...> +struct group_handler; + +template<typename... Exclude, typename... Get, typename... Owned> +struct group_handler<exclude_t<Exclude...>, get_t<Get...>, Owned...> { +// 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<component_traits<Owned>::in_place_delete>...>, "Groups do not support in-place delete"); +std::conditional_t<sizeof...(Owned) == 0, basic_common_type, std::size_t> current{}; + +template<typename Component> +void maybe_valid_if(basic_registry &owner, const Entity entt) { +[[maybe_unused]] const auto cpools = std::forward_as_tuple(owner.assure<Owned>()...); + +const auto is_valid = ((std::is_same_v<Component, Owned> || std::get<storage_type<Owned> &>(cpools).contains(entt)) && ...) +&& ((std::is_same_v<Component, Get> || owner.assure<Get>().contains(entt)) && ...) +&& ((std::is_same_v<Component, Exclude> || !owner.assure<Exclude>().contains(entt)) && ...); + +if constexpr(sizeof...(Owned) == 0) { +if(is_valid && !current.contains(entt)) { +current.emplace(entt); +} +} else { +if(is_valid && !(std::get<0>(cpools).index(entt) < current)) { +const auto pos = current++; +(std::get<storage_type<Owned> &>(cpools).swap_elements(std::get<storage_type<Owned> &>(cpools).data()[pos], entt), ...); +} +} +} + +void discard_if([[maybe_unused]] basic_registry &owner, const Entity entt) { +if constexpr(sizeof...(Owned) == 0) { +current.remove(entt); +} else { +if(const auto cpools = std::forward_as_tuple(owner.assure<Owned>()...); std::get<0>(cpools).contains(entt) && (std::get<0>(cpools).index(entt) < current)) { +const auto pos = --current; +(std::get<storage_type<Owned> &>(cpools).swap_elements(std::get<storage_type<Owned> &>(cpools).data()[pos], entt), ...); +} +} +} +}; + +struct group_data { +std::size_t size; +std::unique_ptr<void, void (*)(void *)> group; +bool (*owned)(const id_type) ENTT_NOEXCEPT; +bool (*get)(const id_type) ENTT_NOEXCEPT; +bool (*exclude)(const id_type) ENTT_NOEXCEPT; +}; + +template<typename Component> +[[nodiscard]] auto &assure(const id_type id = type_hash<Component>::value()) { +static_assert(std::is_same_v<Component, std::decay_t<Component>>, "Non-decayed types not allowed"); +auto &&cpool = pools[id]; + +if(!cpool) { +cpool.reset(new storage_type<Component>{}); +cpool->bind(forward_as_any(*this)); +} + +ENTT_ASSERT(cpool->type() == type_id<Component>(), "Unexpected type"); +return static_cast<storage_type<Component> &>(*cpool); +} + +template<typename Component> +[[nodiscard]] const auto &assure(const id_type id = type_hash<Component>::value()) const { +static_assert(std::is_same_v<Component, std::decay_t<Component>>, "Non-decayed types not allowed"); + +if(const auto it = pools.find(id); it != pools.cend()) { +ENTT_ASSERT(it->second->type() == type_id<Component>(), "Unexpected type"); +return static_cast<const storage_type<Component> &>(*it->second); +} + +static storage_type<Component> placeholder{}; +return placeholder; +} + +auto generate_identifier(const std::size_t pos) ENTT_NOEXCEPT { +ENTT_ASSERT(pos < entity_traits::to_integral(null), "No entities available"); +return entity_traits::combine(static_cast<typename entity_traits::entity_type>(pos), {}); +} + +auto recycle_identifier() ENTT_NOEXCEPT { +ENTT_ASSERT(free_list != null, "No entities available"); +const auto curr = entity_traits::to_entity(free_list); +free_list = entity_traits::combine(entity_traits::to_integral(entities[curr]), tombstone); +return (entities[curr] = entity_traits::combine(curr, entity_traits::to_integral(entities[curr]))); +} + +auto release_entity(const Entity entity, const typename entity_traits::version_type version) { +const typename entity_traits::version_type vers = version + (version == entity_traits::to_version(tombstone)); +entities[entity_traits::to_entity(entity)] = entity_traits::construct(entity_traits::to_integral(free_list), vers); +free_list = entity_traits::combine(entity_traits::to_integral(entity), tombstone); +return vers; +} + +public: +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Underlying version type. */ +using version_type = typename entity_traits::version_type; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Common type among all storage types. */ +using base_type = basic_common_type; +/*! @brief Context type. */ +using context = internal::registry_context; + +/*! @brief Default constructor. */ +basic_registry() +: pools{}, +groups{}, +entities{}, +free_list{tombstone}, +vars{} {} + +/** + * @brief Allocates enough memory upon construction to store `count` pools. + * @param count The number of pools to allocate memory for. + */ +basic_registry(const size_type count) +: pools{}, +groups{}, +entities{}, +free_list{tombstone}, +vars{} { +pools.reserve(count); +} + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +basic_registry(basic_registry &&other) +: pools{std::move(other.pools)}, +groups{std::move(other.groups)}, +entities{std::move(other.entities)}, +free_list{other.free_list}, +vars{std::move(other.vars)} { +for(auto &&curr: pools) { +curr.second->bind(forward_as_any(*this)); +} +} + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This registry. + */ +basic_registry &operator=(basic_registry &&other) { +pools = std::move(other.pools); +groups = std::move(other.groups); +entities = std::move(other.entities); +free_list = other.free_list; +vars = std::move(other.vars); + +for(auto &&curr: pools) { +curr.second->bind(forward_as_any(*this)); +} + +return *this; +} + +/** + * @brief Returns an iterable object to use to _visit_ a registry. + * + * The iterable object returns a pair that contains the name and a reference + * to the current storage. + * + * @return An iterable object to use to _visit_ the registry. + */ +[[nodiscard]] auto storage() ENTT_NOEXCEPT { +return iterable_adaptor{internal::storage_proxy_iterator{pools.begin()}, internal::storage_proxy_iterator{pools.end()}}; +} + +/*! @copydoc storage */ +[[nodiscard]] auto storage() const ENTT_NOEXCEPT { +return iterable_adaptor{internal::storage_proxy_iterator{pools.cbegin()}, internal::storage_proxy_iterator{pools.cend()}}; +} + +/** + * @brief Finds the storage associated with a given name, if any. + * @param id Name used to map the storage within the registry. + * @return An iterator to the given storage if it's found, past the end + * iterator otherwise. + */ +[[nodiscard]] auto storage(const id_type id) { +return internal::storage_proxy_iterator{pools.find(id)}; +} + +/** + * @brief Finds the storage associated with a given name, if any. + * @param id Name used to map the storage within the registry. + * @return An iterator to the given storage if it's found, past the end + * iterator otherwise. + */ +[[nodiscard]] auto storage(const id_type id) const { +return internal::storage_proxy_iterator{pools.find(id)}; +} + +/** + * @brief Returns the storage for a given component type. + * @tparam Component Type of component of which to return the storage. + * @param id Optional name used to map the storage within the registry. + * @return The storage for the given component type. + */ +template<typename Component> +decltype(auto) storage(const id_type id = type_hash<std::remove_const_t<Component>>::value()) { +if constexpr(std::is_const_v<Component>) { +return std::as_const(*this).template storage<std::remove_const_t<Component>>(id); +} else { +return assure<Component>(id); +} +} + +/** + * @brief Returns the storage for a given component type. + * + * @warning + * If a storage for the given component doesn't exist yet, a temporary + * placeholder is returned instead. + * + * @tparam Component Type of component of which to return the storage. + * @param id Optional name used to map the storage within the registry. + * @return The storage for the given component type. + */ +template<typename Component> +decltype(auto) storage(const id_type id = type_hash<std::remove_const_t<Component>>::value()) const { +return assure<std::remove_const_t<Component>>(id); +} + +/** + * @brief Returns the number of entities created so far. + * @return Number of entities created so far. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return entities.size(); +} + +/** + * @brief Returns the number of entities still in use. + * @return Number of entities still in use. + */ +[[nodiscard]] size_type alive() const { +auto sz = entities.size(); + +for(auto curr = free_list; curr != null; --sz) { +curr = entities[entity_traits::to_entity(curr)]; +} + +return sz; +} + +/** + * @brief Increases the capacity (number of entities) of the registry. + * @param cap Desired capacity. + */ +void reserve(const size_type cap) { +entities.reserve(cap); +} + +/** + * @brief Returns the number of entities that a registry has currently + * allocated space for. + * @return Capacity of the registry. + */ +[[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { +return entities.capacity(); +} + +/** + * @brief Checks whether the registry is empty (no entities still in use). + * @return True if the registry is empty, false otherwise. + */ +[[nodiscard]] bool empty() const { +return !alive(); +} + +/** + * @brief Direct access to the list of entities of a registry. + * + * The returned pointer is such that range `[data(), data() + size())` is + * always a valid range, even if the registry is empty. + * + * @warning + * This list contains both valid and destroyed entities and isn't suitable + * for direct use. + * + * @return A pointer to the array of entities. + */ +[[nodiscard]] const entity_type *data() const ENTT_NOEXCEPT { +return entities.data(); +} + +/** + * @brief Returns the head of the list of released entities. + * + * This function is intended for use in conjunction with `assign`.<br/> + * The returned entity has an invalid identifier in all cases. + * + * @return The head of the list of released entities. + */ +[[nodiscard]] entity_type released() const ENTT_NOEXCEPT { +return free_list; +} + +/** + * @brief Checks if an identifier refers to a valid entity. + * @param entity An identifier, either valid or not. + * @return True if the identifier is valid, false otherwise. + */ +[[nodiscard]] bool valid(const entity_type entity) const { +const auto pos = size_type(entity_traits::to_entity(entity)); +return (pos < entities.size() && entities[pos] == entity); +} + +/** + * @brief Returns the actual version for an identifier. + * @param entity A valid identifier. + * @return The version for the given identifier if valid, the tombstone + * version otherwise. + */ +[[nodiscard]] version_type current(const entity_type entity) const { +const auto pos = size_type(entity_traits::to_entity(entity)); +return entity_traits::to_version(pos < entities.size() ? entities[pos] : tombstone); +} + +/** + * @brief Creates a new entity or recycles a destroyed one. + * @return A valid identifier. + */ +[[nodiscard]] entity_type create() { +return (free_list == null) ? entities.emplace_back(generate_identifier(entities.size())) : recycle_identifier(); +} + +/** + * @copybrief create + * + * If the requested entity isn't in use, the suggested identifier is used. + * Otherwise, a new identifier is generated. + * + * @param hint Required identifier. + * @return A valid identifier. + */ +[[nodiscard]] entity_type create(const entity_type hint) { +const auto length = entities.size(); + +if(hint == null || hint == tombstone) { +return create(); +} else if(const auto req = entity_traits::to_entity(hint); !(req < length)) { +entities.resize(size_type(req) + 1u, null); + +for(auto pos = length; pos < req; ++pos) { +release_entity(generate_identifier(pos), {}); +} + +return (entities[req] = hint); +} else if(const auto curr = entity_traits::to_entity(entities[req]); req == curr) { +return create(); +} else { +auto *it = &free_list; +for(; entity_traits::to_entity(*it) != req; it = &entities[entity_traits::to_entity(*it)]) {} +*it = entity_traits::combine(curr, entity_traits::to_integral(*it)); +return (entities[req] = hint); +} +} + +/** + * @brief Assigns each element in a range an identifier. + * + * @sa create + * + * @tparam It Type of 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 create(It first, It last) { +for(; free_list != null && first != last; ++first) { +*first = recycle_identifier(); +} + +const auto length = entities.size(); +entities.resize(length + std::distance(first, last), null); + +for(auto pos = length; first != last; ++first, ++pos) { +*first = entities[pos] = generate_identifier(pos); +} +} + +/** + * @brief Assigns identifiers to an empty registry. + * + * This function is intended for use in conjunction with `data`, `size` and + * `destroyed`.<br/> + * Don't try to inject ranges of randomly generated entities nor the _wrong_ + * head for the list of destroyed entities. There is no guarantee that a + * registry will continue to work properly in this case. + * + * @warning + * There must be no entities still alive for this to work properly. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param destroyed The head of the list of destroyed entities. + */ +template<typename It> +void assign(It first, It last, const entity_type destroyed) { +ENTT_ASSERT(!alive(), "Entities still alive"); +entities.assign(first, last); +free_list = destroyed; +} + +/** + * @brief Releases an identifier. + * + * The version is updated and the identifier can be recycled at any time. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @param entity A valid identifier. + * @return The version of the recycled entity. + */ +version_type release(const entity_type entity) { +return release(entity, static_cast<version_type>(entity_traits::to_version(entity) + 1u)); +} + +/** + * @brief Releases an identifier. + * + * The suggested version or the valid version closest to the suggested one + * is used instead of the implicitly generated version. + * + * @sa release + * + * @param entity A valid identifier. + * @param version A desired version upon destruction. + * @return The version actually assigned to the entity. + */ +version_type release(const entity_type entity, const version_type version) { +ENTT_ASSERT(orphan(entity), "Non-orphan entity"); +return release_entity(entity, version); +} + +/** + * @brief Releases all identifiers in a range. + * + * @sa release + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ +template<typename It> +void release(It first, It last) { +for(; first != last; ++first) { +release(*first); +} +} + +/** + * @brief Destroys an entity and releases its identifier. + * + * @sa release + * + * @warning + * Adding or removing components to an entity that is being destroyed can + * result in undefined behavior. Attempting to use an invalid entity results + * in undefined behavior. + * + * @param entity A valid identifier. + * @return The version of the recycled entity. + */ +version_type destroy(const entity_type entity) { +return destroy(entity, static_cast<version_type>(entity_traits::to_version(entity) + 1u)); +} + +/** + * @brief Destroys an entity and releases its identifier. + * + * The suggested version or the valid version closest to the suggested one + * is used instead of the implicitly generated version. + * + * @sa destroy + * + * @param entity A valid identifier. + * @param version A desired version upon destruction. + * @return The version actually assigned to the entity. + */ +version_type destroy(const entity_type entity, const version_type version) { +ENTT_ASSERT(valid(entity), "Invalid entity"); + +for(size_type pos = pools.size(); pos; --pos) { +pools.begin()[pos - 1u].second->remove(entity); +} + +return release_entity(entity, version); +} + +/** + * @brief Destroys all entities in a range and releases their identifiers. + * + * @sa destroy + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ +template<typename It> +void destroy(It first, It last) { +for(; first != last; ++first) { +destroy(*first); +} +} + +/** + * @brief Assigns the given component to an entity. + * + * The component must have a proper constructor or be of aggregate type. + * + * @warning + * Attempting to use an invalid entity or to assign a component to an entity + * that already owns it results in undefined behavior. + * + * @tparam Component Type of component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ +template<typename Component, typename... Args> +decltype(auto) emplace(const entity_type entity, Args &&...args) { +ENTT_ASSERT(valid(entity), "Invalid entity"); +return assure<Component>().emplace(entity, std::forward<Args>(args)...); +} + +/** + * @brief Assigns each entity in a range the given component. + * + * @sa emplace + * + * @tparam Component Type of component to create. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param value An instance of the component to assign. + */ +template<typename Component, typename It> +void insert(It first, It last, const Component &value = {}) { +ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); +assure<Component>().insert(first, last, value); +} + +/** + * @brief Assigns each entity in a range the given components. + * + * @sa emplace + * + * @tparam Component Type of component to create. + * @tparam EIt Type of input iterator. + * @tparam CIt Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param from An iterator to the first element of the range of components. + */ +template<typename Component, typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, Component>>> +void insert(EIt first, EIt last, CIt from) { +ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); +assure<Component>().insert(first, last, from); +} + +/** + * @brief Assigns or replaces the given component for an entity. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Type of component to assign or replace. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ +template<typename Component, typename... Args> +decltype(auto) emplace_or_replace(const entity_type entity, Args &&...args) { +ENTT_ASSERT(valid(entity), "Invalid entity"); +auto &cpool = assure<Component>(); + +return cpool.contains(entity) +? cpool.patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward<Args>(args)...}), ...); }) +: cpool.emplace(entity, std::forward<Args>(args)...); +} + +/** + * @brief Patches the given component for an entity. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(Component &); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned. However, this function can be used to trigger an update signal + * for them. + * + * @warning + * Attempting to use an invalid entity or to patch a component of an entity + * that doesn't own it results in undefined behavior. + * + * @tparam Component Type of component to patch. + * @tparam Func Types of the function objects to invoke. + * @param entity A valid identifier. + * @param func Valid function objects. + * @return A reference to the patched component. + */ +template<typename Component, typename... Func> +decltype(auto) patch(const entity_type entity, Func &&...func) { +ENTT_ASSERT(valid(entity), "Invalid entity"); +return assure<Component>().patch(entity, std::forward<Func>(func)...); +} + +/** + * @brief Replaces the given component for an entity. + * + * The component must have a proper constructor or be of aggregate type. + * + * @warning + * Attempting to use an invalid entity or to replace a component of an + * entity that doesn't own it results in undefined behavior. + * + * @tparam Component Type of component to replace. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the component being replaced. + */ +template<typename Component, typename... Args> +decltype(auto) replace(const entity_type entity, Args &&...args) { +ENTT_ASSERT(valid(entity), "Invalid entity"); +return assure<Component>().patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward<Args>(args)...}), ...); }); +} + +/** + * @brief Removes the given components from an entity. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Type of component to remove. + * @tparam Other Other types of components to remove. + * @param entity A valid identifier. + * @return The number of components actually removed. + */ +template<typename Component, typename... Other> +size_type remove(const entity_type entity) { +ENTT_ASSERT(valid(entity), "Invalid entity"); +return (assure<Component>().remove(entity) + ... + assure<Other>().remove(entity)); +} + +/** + * @brief Removes the given components from all the entities in a range. + * + * @sa remove + * + * @tparam Component Type of component to remove. + * @tparam Other Other types of components to remove. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return The number of components actually removed. + */ +template<typename Component, typename... Other, typename It> +size_type remove(It first, It last) { +if constexpr(sizeof...(Other) == 0u) { +ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); +return assure<Component>().remove(std::move(first), std::move(last)); +} else { +size_type count{}; + +for(auto cpools = std::forward_as_tuple(assure<Component>(), assure<Other>()...); first != last; ++first) { +ENTT_ASSERT(valid(*first), "Invalid entity"); +count += std::apply([entt = *first](auto &...curr) { return (curr.remove(entt) + ... + 0u); }, cpools); +} + +return count; +} +} + +/** + * @brief Erases the given components from an entity. + * + * @warning + * Attempting to use an invalid entity or to erase a component from an + * entity that doesn't own it results in undefined behavior. + * + * @tparam Component Types of components to erase. + * @tparam Other Other types of components to erase. + * @param entity A valid identifier. + */ +template<typename Component, typename... Other> +void erase(const entity_type entity) { +ENTT_ASSERT(valid(entity), "Invalid entity"); +(assure<Component>().erase(entity), (assure<Other>().erase(entity), ...)); +} + +/** + * @brief Erases the given components from all the entities in a range. + * + * @sa erase + * + * @tparam Component Types of components to erase. + * @tparam Other Other types of components to erase. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ +template<typename Component, typename... Other, typename It> +void erase(It first, It last) { +if constexpr(sizeof...(Other) == 0u) { +ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); +assure<Component>().erase(std::move(first), std::move(last)); +} else { +for(auto cpools = std::forward_as_tuple(assure<Component>(), assure<Other>()...); first != last; ++first) { +ENTT_ASSERT(valid(*first), "Invalid entity"); +std::apply([entt = *first](auto &...curr) { (curr.erase(entt), ...); }, cpools); +} +} +} + +/** + * @brief Removes all tombstones from a registry or only the pools for the + * given components. + * @tparam Component Types of components for which to clear all tombstones. + */ +template<typename... Component> +void compact() { +if constexpr(sizeof...(Component) == 0) { +for(auto &&curr: pools) { +curr.second->compact(); +} +} else { +(assure<Component>().compact(), ...); +} +} + +/** + * @brief Checks if an entity has all the given components. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Components for which to perform the check. + * @param entity A valid identifier. + * @return True if the entity has all the components, false otherwise. + */ +template<typename... Component> +[[nodiscard]] bool all_of(const entity_type entity) const { +ENTT_ASSERT(valid(entity), "Invalid entity"); +return (assure<std::remove_const_t<Component>>().contains(entity) && ...); +} + +/** + * @brief Checks if an entity has at least one of the given components. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Components for which to perform the check. + * @param entity A valid identifier. + * @return True if the entity has at least one of the given components, + * false otherwise. + */ +template<typename... Component> +[[nodiscard]] bool any_of(const entity_type entity) const { +ENTT_ASSERT(valid(entity), "Invalid entity"); +return (assure<std::remove_const_t<Component>>().contains(entity) || ...); +} + +/** + * @brief Returns references to the given components for an entity. + * + * @warning + * Attempting to use an invalid entity or to get a component from an entity + * that doesn't own it results in undefined behavior. + * + * @tparam Component Types of components to get. + * @param entity A valid identifier. + * @return References to the components owned by the entity. + */ +template<typename... Component> +[[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) const { +ENTT_ASSERT(valid(entity), "Invalid entity"); +return view<Component...>().template get<const Component...>(entity); +} + +/*! @copydoc get */ +template<typename... Component> +[[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) { +ENTT_ASSERT(valid(entity), "Invalid entity"); +return view<Component...>().template get<Component...>(entity); +} + +/** + * @brief Returns a reference to the given component for an entity. + * + * In case the entity doesn't own the component, the parameters provided are + * used to construct it. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Type of component to get. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid identifier. + * @param args Parameters to use to initialize the component. + * @return Reference to the component owned by the entity. + */ +template<typename Component, typename... Args> +[[nodiscard]] decltype(auto) get_or_emplace(const entity_type entity, Args &&...args) { +ENTT_ASSERT(valid(entity), "Invalid entity"); +auto &cpool = assure<Component>(); +return cpool.contains(entity) ? cpool.get(entity) : cpool.emplace(entity, std::forward<Args>(args)...); +} + +/** + * @brief Returns pointers to the given components for an entity. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @note + * The registry retains ownership of the pointed-to components. + * + * @tparam Component Types of components to get. + * @param entity A valid identifier. + * @return Pointers to the components owned by the entity. + */ +template<typename... Component> +[[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) const { +ENTT_ASSERT(valid(entity), "Invalid entity"); + +if constexpr(sizeof...(Component) == 1) { +const auto &cpool = assure<std::remove_const_t<Component>...>(); +return cpool.contains(entity) ? std::addressof(cpool.get(entity)) : nullptr; +} else { +return std::make_tuple(try_get<Component>(entity)...); +} +} + +/*! @copydoc try_get */ +template<typename... Component> +[[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) { +if constexpr(sizeof...(Component) == 1) { +return (const_cast<Component *>(std::as_const(*this).template try_get<Component>(entity)), ...); +} else { +return std::make_tuple(try_get<Component>(entity)...); +} +} + +/** + * @brief Clears a whole registry or the pools for the given components. + * @tparam Component Types of components to remove from their entities. + */ +template<typename... Component> +void clear() { +if constexpr(sizeof...(Component) == 0) { +for(auto &&curr: pools) { +curr.second->clear(); +} + +each([this](const auto entity) { this->release(entity); }); +} else { +(assure<Component>().clear(), ...); +} +} + +/** + * @brief Iterates all the entities that are still in use. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const Entity); + * @endcode + * + * It's not defined whether entities created during iteration are returned. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ +template<typename Func> +void each(Func func) const { +if(free_list == null) { +for(auto pos = entities.size(); pos; --pos) { +func(entities[pos - 1]); +} +} else { +for(auto pos = entities.size(); pos; --pos) { +if(const auto entity = entities[pos - 1]; entity_traits::to_entity(entity) == (pos - 1)) { +func(entity); +} +} +} +} + +/** + * @brief Checks if an entity has components assigned. + * @param entity A valid identifier. + * @return True if the entity has no components assigned, false otherwise. + */ +[[nodiscard]] bool orphan(const entity_type entity) const { +ENTT_ASSERT(valid(entity), "Invalid entity"); +return std::none_of(pools.cbegin(), pools.cend(), [entity](auto &&curr) { return curr.second->contains(entity); }); +} + +/** + * @brief Returns a sink object for the given component. + * + * Use this function to receive notifications whenever a new instance of the + * given component is created and assigned to an entity.<br/> + * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry<Entity> &, Entity); + * @endcode + * + * Listeners are invoked **after** assigning the component to the entity. + * + * @sa sink + * + * @tparam Component Type of component of which to get the sink. + * @return A temporary sink object. + */ +template<typename Component> +[[nodiscard]] auto on_construct() { +return assure<Component>().on_construct(); +} + +/** + * @brief Returns a sink object for the given component. + * + * Use this function to receive notifications whenever an instance of the + * given component is explicitly updated.<br/> + * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry<Entity> &, Entity); + * @endcode + * + * Listeners are invoked **after** updating the component. + * + * @sa sink + * + * @tparam Component Type of component of which to get the sink. + * @return A temporary sink object. + */ +template<typename Component> +[[nodiscard]] auto on_update() { +return assure<Component>().on_update(); +} + +/** + * @brief Returns a sink object for the given component. + * + * Use this function to receive notifications whenever an instance of the + * given component is removed from an entity and thus destroyed.<br/> + * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry<Entity> &, Entity); + * @endcode + * + * Listeners are invoked **before** removing the component from the entity. + * + * @sa sink + * + * @tparam Component Type of component of which to get the sink. + * @return A temporary sink object. + */ +template<typename Component> +[[nodiscard]] auto on_destroy() { +return assure<Component>().on_destroy(); +} + +/** + * @brief Returns a view for the given components. + * + * Views are created on the fly and share with the registry its internal + * data structures. Feel free to discard them after the use.<br/> + * Creating and destroying a view is an incredibly cheap operation. As a + * rule of thumb, storing a view should never be an option. + * + * @tparam Component Type of component used to construct the view. + * @tparam Other Other types of components used to construct the view. + * @tparam Exclude Types of components used to filter the view. + * @return A newly created view. + */ +template<typename Component, typename... Other, typename... Exclude> +[[nodiscard]] basic_view<entity_type, get_t<std::add_const_t<Component>, std::add_const_t<Other>...>, exclude_t<Exclude...>> view(exclude_t<Exclude...> = {}) const { +return {assure<std::remove_const_t<Component>>(), assure<std::remove_const_t<Other>>()..., assure<Exclude>()...}; +} + +/*! @copydoc view */ +template<typename Component, typename... Other, typename... Exclude> +[[nodiscard]] basic_view<entity_type, get_t<Component, Other...>, exclude_t<Exclude...>> view(exclude_t<Exclude...> = {}) { +return {assure<std::remove_const_t<Component>>(), assure<std::remove_const_t<Other>>()..., assure<Exclude>()...}; +} + +/** + * @brief Returns a group for the given components. + * + * Groups are created on the fly and share with the registry its internal + * data structures. Feel free to discard them after the use.<br/> + * Creating and destroying a group is an incredibly cheap operation. As a + * rule of thumb, storing a group should never be an option. + * + * Groups support exclusion lists and can own types of components. The more + * types are owned by a group, the faster it is to iterate entities and + * components.<br/> + * However, groups also affect some features of the registry such as the + * creation and destruction of components. + * + * @note + * Pools of components that are owned by a group cannot be sorted anymore. + * The group takes the ownership of the pools and arrange components so as + * to iterate them as fast as possible. + * + * @tparam Owned Types of components owned by the group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + * @return A newly created group. + */ +template<typename... Owned, typename... Get, typename... Exclude> +[[nodiscard]] basic_group<entity_type, owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> group(get_t<Get...>, exclude_t<Exclude...> = {}) { +static_assert(sizeof...(Owned) + sizeof...(Get) > 0, "Exclusion-only groups are not supported"); +static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1, "Single component groups are not allowed"); + +using handler_type = group_handler<exclude_t<std::remove_const_t<Exclude>...>, get_t<std::remove_const_t<Get>...>, std::remove_const_t<Owned>...>; + +const auto cpools = std::forward_as_tuple(assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()...); +constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); +handler_type *handler = nullptr; + +auto it = std::find_if(groups.cbegin(), groups.cend(), [size](const auto &gdata) { +return gdata.size == size +&& (gdata.owned(type_hash<std::remove_const_t<Owned>>::value()) && ...) +&& (gdata.get(type_hash<std::remove_const_t<Get>>::value()) && ...) +&& (gdata.exclude(type_hash<Exclude>::value()) && ...); +}); + +if(it != groups.cend()) { +handler = static_cast<handler_type *>(it->group.get()); +} else { +group_data candidate = { +size, +{new handler_type{}, [](void *instance) { delete static_cast<handler_type *>(instance); }}, +[]([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash<std::remove_const_t<Owned>>::value()) || ...); }, +[]([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash<std::remove_const_t<Get>>::value()) || ...); }, +[]([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash<Exclude>::value()) || ...); }, +}; + +handler = static_cast<handler_type *>(candidate.group.get()); + +const void *maybe_valid_if = nullptr; +const void *discard_if = nullptr; + +if constexpr(sizeof...(Owned) == 0) { +groups.push_back(std::move(candidate)); +} else { +[[maybe_unused]] auto has_conflict = [size](const auto &gdata) { +const auto overlapping = (0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value())); +const auto sz = overlapping + (0u + ... + gdata.get(type_hash<std::remove_const_t<Get>>::value())) + (0u + ... + gdata.exclude(type_hash<Exclude>::value())); +return !overlapping || ((sz == size) || (sz == gdata.size)); +}; + +ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), std::move(has_conflict)), "Conflicting groups"); + +const auto next = std::find_if_not(groups.cbegin(), groups.cend(), [size](const auto &gdata) { +return !(0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value())) || (size > gdata.size); +}); + +const auto prev = std::find_if(std::make_reverse_iterator(next), groups.crend(), [](const auto &gdata) { +return (0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value())); +}); + +maybe_valid_if = (next == groups.cend() ? maybe_valid_if : next->group.get()); +discard_if = (prev == groups.crend() ? discard_if : prev->group.get()); +groups.insert(next, std::move(candidate)); +} + +(on_construct<std::remove_const_t<Owned>>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<std::remove_const_t<Owned>>>(*handler), ...); +(on_construct<std::remove_const_t<Get>>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<std::remove_const_t<Get>>>(*handler), ...); +(on_destroy<Exclude>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<Exclude>>(*handler), ...); + +(on_destroy<std::remove_const_t<Owned>>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); +(on_destroy<std::remove_const_t<Get>>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); +(on_construct<Exclude>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); + +if constexpr(sizeof...(Owned) == 0) { +for(const auto entity: view<Owned..., Get...>(exclude<Exclude...>)) { +handler->current.emplace(entity); +} +} else { +// we cannot iterate backwards because we want to leave behind valid entities in case of owned types +for(auto *first = std::get<0>(cpools).data(), *last = first + std::get<0>(cpools).size(); first != last; ++first) { +handler->template maybe_valid_if<type_list_element_t<0, type_list<std::remove_const_t<Owned>...>>>(*this, *first); +} +} +} + +return {handler->current, std::get<storage_type<std::remove_const_t<Owned>> &>(cpools)..., std::get<storage_type<std::remove_const_t<Get>> &>(cpools)...}; +} + +/*! @copydoc group */ +template<typename... Owned, typename... Get, typename... Exclude> +[[nodiscard]] basic_group<entity_type, owned_t<std::add_const_t<Owned>...>, get_t<std::add_const_t<Get>...>, exclude_t<Exclude...>> group_if_exists(get_t<Get...>, exclude_t<Exclude...> = {}) const { +auto it = std::find_if(groups.cbegin(), groups.cend(), [](const auto &gdata) { +return gdata.size == (sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude)) +&& (gdata.owned(type_hash<std::remove_const_t<Owned>>::value()) && ...) +&& (gdata.get(type_hash<std::remove_const_t<Get>>::value()) && ...) +&& (gdata.exclude(type_hash<Exclude>::value()) && ...); +}); + +if(it == groups.cend()) { +return {}; +} else { +using handler_type = group_handler<exclude_t<std::remove_const_t<Exclude>...>, get_t<std::remove_const_t<Get>...>, std::remove_const_t<Owned>...>; +return {static_cast<handler_type *>(it->group.get())->current, assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()...}; +} +} + +/*! @copydoc group */ +template<typename... Owned, typename... Exclude> +[[nodiscard]] basic_group<entity_type, owned_t<Owned...>, get_t<>, exclude_t<Exclude...>> group(exclude_t<Exclude...> = {}) { +return group<Owned...>(get_t<>{}, exclude<Exclude...>); +} + +/*! @copydoc group */ +template<typename... Owned, typename... Exclude> +[[nodiscard]] basic_group<entity_type, owned_t<std::add_const_t<Owned>...>, get_t<>, exclude_t<Exclude...>> group_if_exists(exclude_t<Exclude...> = {}) const { +return group_if_exists<std::add_const_t<Owned>...>(get_t<>{}, exclude<Exclude...>); +} + +/** + * @brief Checks whether the given components belong to any group. + * @tparam Component Types of components in which one is interested. + * @return True if the pools of the given components are _free_, false + * otherwise. + */ +template<typename... Component> +[[nodiscard]] bool owned() const { +return std::any_of(groups.cbegin(), groups.cend(), [](auto &&gdata) { return (gdata.owned(type_hash<std::remove_const_t<Component>>::value()) || ...); }); +} + +/** + * @brief Checks whether a group can be sorted. + * @tparam Owned Types of components owned by the group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + * @return True if the group can be sorted, false otherwise. + */ +template<typename... Owned, typename... Get, typename... Exclude> +[[nodiscard]] bool sortable(const basic_group<entity_type, owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> &) ENTT_NOEXCEPT { +constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); +auto pred = [size](const auto &gdata) { return (0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value())) && (size < gdata.size); }; +return std::find_if(groups.cbegin(), groups.cend(), std::move(pred)) == groups.cend(); +} + +/** + * @brief Sorts the elements of a given component. + * + * The order remains valid until a component of the given type is assigned + * to or removed from an entity.<br/> + * The comparison function object returns `true` if the first element is + * _less_ than the second one, `false` otherwise. Its signature is also + * equivalent to one of the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * bool(const Component &, const Component &); + * @endcode + * + * Moreover, it shall induce a _strict weak ordering_ on the values.<br/> + * The sort function object offers an `operator()` that accepts: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function object to use to compare the elements. + * + * The comparison function object hasn't necessarily the type of the one + * passed along with the other parameters to this member function. + * + * @warning + * Pools of components owned by a group cannot be sorted. + * + * @tparam Component Type of components to sort. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ +template<typename Component, typename Compare, typename Sort = std_sort, typename... Args> +void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { +ENTT_ASSERT(!owned<Component>(), "Cannot sort owned storage"); +auto &cpool = assure<Component>(); + +if constexpr(std::is_invocable_v<Compare, decltype(cpool.get({})), decltype(cpool.get({}))>) { +auto comp = [&cpool, compare = std::move(compare)](const auto lhs, const auto rhs) { return compare(std::as_const(cpool.get(lhs)), std::as_const(cpool.get(rhs))); }; +cpool.sort(std::move(comp), std::move(algo), std::forward<Args>(args)...); +} else { +cpool.sort(std::move(compare), std::move(algo), std::forward<Args>(args)...); +} +} + +/** + * @brief Sorts two pools of components in the same way. + * + * Being `To` and `From` the two sets, after invoking this function an + * iterator for `To` returns elements according to the following rules: + * + * * All entities in `To` that are also in `From` are returned first + * according to the order they have in `From`. + * * All entities in `To` that are not in `From` are returned in no + * particular order after all the other entities. + * + * Any subsequent change to `From` won't affect the order in `To`. + * + * @warning + * Pools of components owned by a group cannot be sorted. + * + * @tparam To Type of components to sort. + * @tparam From Type of components to use to sort. + */ +template<typename To, typename From> +void sort() { +ENTT_ASSERT(!owned<To>(), "Cannot sort owned storage"); +assure<To>().respect(assure<From>()); +} + +/** + * @brief Returns the context object, that is, a general purpose container. + * @return The context object, that is, a general purpose container. + */ +context &ctx() ENTT_NOEXCEPT { +return vars; +} + +/*! @copydoc ctx */ +const context &ctx() const ENTT_NOEXCEPT { +return vars; +} + +private: +dense_map<id_type, std::unique_ptr<base_type>, identity> pools; +std::vector<group_data> groups; +std::vector<entity_type> entities; +entity_type free_list; +context vars; +}; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Non-owning handle to an entity. + * + * Tiny wrapper around a registry and an entity. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Types to which to restrict the scope of a handle. + */ +template<typename Entity, typename... Type> +struct basic_handle { +/*! @brief Type of registry accepted by the handle. */ +using registry_type = constness_as_t<basic_registry<std::remove_const_t<Entity>>, Entity>; +/*! @brief Underlying entity identifier. */ +using entity_type = typename registry_type::entity_type; +/*! @brief Underlying version type. */ +using version_type = typename registry_type::version_type; +/*! @brief Unsigned integer type. */ +using size_type = typename registry_type::size_type; + +/*! @brief Constructs an invalid handle. */ +basic_handle() ENTT_NOEXCEPT +: reg{}, +entt{null} {} + +/** + * @brief Constructs a handle from a given registry and entity. + * @param ref An instance of the registry class. + * @param value A valid identifier. + */ +basic_handle(registry_type &ref, entity_type value) ENTT_NOEXCEPT +: reg{&ref}, +entt{value} {} + +/** + * @brief Constructs a const handle from a non-const one. + * @tparam Other A valid entity type (see entt_traits for more details). + * @tparam Args Scope of the handle to construct. + * @return A const handle referring to the same registry and the same + * entity. + */ +template<typename Other, typename... Args> +operator basic_handle<Other, Args...>() const ENTT_NOEXCEPT { +static_assert(std::is_same_v<Other, Entity> || std::is_same_v<std::remove_const_t<Other>, Entity>, "Invalid conversion between different handles"); +static_assert((sizeof...(Type) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Type)) && ... && (type_list_contains_v<type_list<Type...>, Args>))), "Invalid conversion between different handles"); + +return reg ? basic_handle<Other, Args...>{*reg, entt} : basic_handle<Other, Args...>{}; +} + +/** + * @brief Converts a handle to its underlying entity. + * @return The contained identifier. + */ +[[nodiscard]] operator entity_type() const ENTT_NOEXCEPT { +return entity(); +} + +/** + * @brief Checks if a handle refers to non-null registry pointer and entity. + * @return True if the handle refers to non-null registry and entity, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return reg && reg->valid(entt); +} + +/** + * @brief Checks if a handle refers to a valid entity or not. + * @return True if the handle refers to a valid entity, false otherwise. + */ +[[nodiscard]] bool valid() const { +return reg->valid(entt); +} + +/** + * @brief Returns a pointer to the underlying registry, if any. + * @return A pointer to the underlying registry, if any. + */ +[[nodiscard]] registry_type *registry() const ENTT_NOEXCEPT { +return reg; +} + +/** + * @brief Returns the entity associated with a handle. + * @return The entity associated with the handle. + */ +[[nodiscard]] entity_type entity() const ENTT_NOEXCEPT { +return entt; +} + +/** + * @brief Destroys the entity associated with a handle. + * @sa basic_registry::destroy + */ +void destroy() { +reg->destroy(entt); +} + +/** + * @brief Destroys the entity associated with a handle. + * @sa basic_registry::destroy + * @param version A desired version upon destruction. + */ +void destroy(const version_type version) { +reg->destroy(entt, version); +} + +/** + * @brief Assigns the given component to a handle. + * @sa basic_registry::emplace + * @tparam Component Type of component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ +template<typename Component, typename... Args> +decltype(auto) emplace(Args &&...args) const { +static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type"); +return reg->template emplace<Component>(entt, std::forward<Args>(args)...); +} + +/** + * @brief Assigns or replaces the given component for a handle. + * @sa basic_registry::emplace_or_replace + * @tparam Component Type of component to assign or replace. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ +template<typename Component, typename... Args> +decltype(auto) emplace_or_replace(Args &&...args) const { +static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type"); +return reg->template emplace_or_replace<Component>(entt, std::forward<Args>(args)...); +} + +/** + * @brief Patches the given component for a handle. + * @sa basic_registry::patch + * @tparam Component Type of component to patch. + * @tparam Func Types of the function objects to invoke. + * @param func Valid function objects. + * @return A reference to the patched component. + */ +template<typename Component, typename... Func> +decltype(auto) patch(Func &&...func) const { +static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type"); +return reg->template patch<Component>(entt, std::forward<Func>(func)...); +} + +/** + * @brief Replaces the given component for a handle. + * @sa basic_registry::replace + * @tparam Component Type of component to replace. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the component being replaced. + */ +template<typename Component, typename... Args> +decltype(auto) replace(Args &&...args) const { +static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type"); +return reg->template replace<Component>(entt, std::forward<Args>(args)...); +} + +/** + * @brief Removes the given components from a handle. + * @sa basic_registry::remove + * @tparam Component Types of components to remove. + * @return The number of components actually removed. + */ +template<typename... Component> +size_type remove() const { +static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type"); +return reg->template remove<Component...>(entt); +} + +/** + * @brief Erases the given components from a handle. + * @sa basic_registry::erase + * @tparam Component Types of components to erase. + */ +template<typename... Component> +void erase() const { +static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type"); +reg->template erase<Component...>(entt); +} + +/** + * @brief Checks if a handle has all the given components. + * @sa basic_registry::all_of + * @tparam Component Components for which to perform the check. + * @return True if the handle has all the components, false otherwise. + */ +template<typename... Component> +[[nodiscard]] decltype(auto) all_of() const { +return reg->template all_of<Component...>(entt); +} + +/** + * @brief Checks if a handle has at least one of the given components. + * @sa basic_registry::any_of + * @tparam Component Components for which to perform the check. + * @return True if the handle has at least one of the given components, + * false otherwise. + */ +template<typename... Component> +[[nodiscard]] decltype(auto) any_of() const { +return reg->template any_of<Component...>(entt); +} + +/** + * @brief Returns references to the given components for a handle. + * @sa basic_registry::get + * @tparam Component Types of components to get. + * @return References to the components owned by the handle. + */ +template<typename... Component> +[[nodiscard]] decltype(auto) get() const { +static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type"); +return reg->template get<Component...>(entt); +} + +/** + * @brief Returns a reference to the given component for a handle. + * @sa basic_registry::get_or_emplace + * @tparam Component Type of component to get. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return Reference to the component owned by the handle. + */ +template<typename Component, typename... Args> +[[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const { +static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type"); +return reg->template get_or_emplace<Component>(entt, std::forward<Args>(args)...); +} + +/** + * @brief Returns pointers to the given components for a handle. + * @sa basic_registry::try_get + * @tparam Component Types of components to get. + * @return Pointers to the components owned by the handle. + */ +template<typename... Component> +[[nodiscard]] auto try_get() const { +static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type"); +return reg->template try_get<Component...>(entt); +} + +/** + * @brief Checks if a handle has components assigned. + * @return True if the handle has no components assigned, false otherwise. + */ +[[nodiscard]] bool orphan() const { +return reg->orphan(entt); +} + +/** + * @brief Visits a handle and returns the pools for its components. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(id_type, const basic_sparse_set<entity_type> &); + * @endcode + * + * Returned pools are those that contain the entity associated with the + * handle. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ +template<typename Func> +void visit(Func &&func) const { +for(auto [id, storage]: reg->storage()) { +if(storage.contains(entt)) { +func(id, storage); +} +} +} + +private: +registry_type *reg; +entity_type entt; +}; + +/** + * @brief Compares two handles. + * @tparam Args Scope of the first handle. + * @tparam Other Scope of the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if both handles refer to the same registry and the same + * entity, false otherwise. + */ +template<typename... Args, typename... Other> +[[nodiscard]] bool operator==(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) ENTT_NOEXCEPT { +return lhs.registry() == rhs.registry() && lhs.entity() == rhs.entity(); +} + +/** + * @brief Compares two handles. + * @tparam Args Scope of the first handle. + * @tparam Other Scope of the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return False if both handles refer to the same registry and the same + * entity, true otherwise. + */ +template<typename... Args, typename... Other> +[[nodiscard]] bool operator!=(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +basic_handle(basic_registry<Entity> &, Entity) -> basic_handle<Entity>; + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +basic_handle(const basic_registry<Entity> &, Entity) -> basic_handle<const Entity>; + +} // namespace entt + +#endif + +// #include "entity/helper.hpp" +#ifndef ENTT_ENTITY_HELPER_HPP +#define ENTT_ENTITY_HELPER_HPP + +#include <type_traits> +// #include "../config/config.h" + +// #include "../core/fwd.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../signal/delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + +#include <cstddef> +#include <functional> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Ret, typename... Args> +auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template<typename Ret, typename Type, typename... Args, typename Other> +auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template<typename Class, typename Ret, typename... Args, typename... Other> +auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template<typename Class, typename Ret, typename... Args, typename... Other> +auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template<typename Class, typename Type, typename... Other> +auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template<typename... Type> +using function_pointer_t = decltype(internal::function_pointer(std::declval<Type>()...)); + +template<typename... Class, typename Ret, typename... Args> +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { +return std::index_sequence_for<Class..., Args...>{}; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @brief Used to wrap a function or a member of a specified type. */ +template<auto> +struct connect_arg_t {}; + +/*! @brief Constant of type connect_arg_t used to disambiguate calls. */ +template<auto Func> +inline constexpr connect_arg_t<Func> connect_arg{}; + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template<typename> +class delegate; + +/** + * @brief Utility class to use to send around functions and members. + * + * Unmanaged delegate for function pointers and members. Users of this class are + * in charge of disconnecting instances before deleting them. + * + * A delegate can be used as a general purpose invoker without memory overhead + * for free functions possibly with payloads and bound or unbound members. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template<typename Ret, typename... Args> +class delegate<Ret(Args...)> { +template<auto Candidate, std::size_t... Index> +[[nodiscard]] auto wrap(std::index_sequence<Index...>) ENTT_NOEXCEPT { +return [](const void *, Args... args) -> Ret { +[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...); +return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...)); +}; +} + +template<auto Candidate, typename Type, std::size_t... Index> +[[nodiscard]] auto wrap(Type &, std::index_sequence<Index...>) ENTT_NOEXCEPT { +return [](const void *payload, Args... args) -> Ret { +[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...); +Type *curr = static_cast<Type *>(const_cast<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))...)); +}; +} + +template<auto Candidate, typename Type, std::size_t... Index> +[[nodiscard]] auto wrap(Type *, std::index_sequence<Index...>) ENTT_NOEXCEPT { +return [](const void *payload, Args... args) -> Ret { +[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...); +Type *curr = static_cast<Type *>(const_cast<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))...)); +}; +} + +public: +/*! @brief Function type of the contained target. */ +using function_type = Ret(const void *, Args...); +/*! @brief Function type of the delegate. */ +using type = Ret(Args...); +/*! @brief Return type of the delegate. */ +using result_type = Ret; + +/*! @brief Default constructor. */ +delegate() ENTT_NOEXCEPT +: instance{nullptr}, +fn{nullptr} {} + +/** + * @brief Constructs a delegate and connects a free function or an unbound + * member. + * @tparam Candidate Function or member to connect to the delegate. + */ +template<auto Candidate> +delegate(connect_arg_t<Candidate>) ENTT_NOEXCEPT { +connect<Candidate>(); +} + +/** + * @brief Constructs a delegate and connects a free function with payload or + * a bound member. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ +template<auto Candidate, typename Type> +delegate(connect_arg_t<Candidate>, Type &&value_or_instance) ENTT_NOEXCEPT { +connect<Candidate>(std::forward<Type>(value_or_instance)); +} + +/** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ +delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { +connect(function, payload); +} + +/** + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. + */ +template<auto Candidate> +void connect() ENTT_NOEXCEPT { +instance = nullptr; + +if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) { +fn = [](const void *, Args... args) -> Ret { +return Ret(std::invoke(Candidate, std::forward<Args>(args)...)); +}; +} else if constexpr(std::is_member_pointer_v<decltype(Candidate)>) { +fn = wrap<Candidate>(internal::index_sequence_for<type_list_element_t<0, type_list<Args...>>>(internal::function_pointer_t<decltype(Candidate)>{})); +} else { +fn = wrap<Candidate>(internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate)>{})); +} +} + +/** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the delegate.<br/> + * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the delegate itself. + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid reference that fits the purpose. + */ +template<auto Candidate, typename Type> +void connect(Type &value_or_instance) ENTT_NOEXCEPT { +instance = &value_or_instance; + +if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, Args...>) { +fn = [](const void *payload, Args... args) -> Ret { +Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload)); +return Ret(std::invoke(Candidate, *curr, std::forward<Args>(args)...)); +}; +} else { +fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{})); +} +} + +/** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * @sa connect(Type &) + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + */ +template<auto Candidate, typename Type> +void connect(Type *value_or_instance) ENTT_NOEXCEPT { +instance = value_or_instance; + +if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, Args...>) { +fn = [](const void *payload, Args... args) -> Ret { +Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload)); +return Ret(std::invoke(Candidate, curr, std::forward<Args>(args)...)); +}; +} else { +fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{})); +} +} + +/** + * @brief Connects an user defined function with optional payload to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of an instance overcomes + * the one of the delegate.<br/> + * The payload is returned as the first argument to the target function in + * all cases. + * + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ +void connect(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { +instance = payload; +fn = function; +} + +/** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ +void reset() ENTT_NOEXCEPT { +instance = nullptr; +fn = nullptr; +} + +/** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ +[[nodiscard]] const void *data() const ENTT_NOEXCEPT { +return instance; +} + +/** + * @brief Triggers a delegate. + * + * The delegate invokes the underlying function and returns the result. + * + * @warning + * Attempting to trigger an invalid delegate results in undefined + * behavior. + * + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. + */ +Ret operator()(Args... args) const { +ENTT_ASSERT(static_cast<bool>(*this), "Uninitialized delegate"); +return fn(instance, std::forward<Args>(args)...); +} + +/** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +// no need to also test instance +return !(fn == nullptr); +} + +/** + * @brief Compares the contents of two delegates. + * @param other Delegate with which to compare. + * @return False if the two contents differ, true otherwise. + */ +[[nodiscard]] bool operator==(const delegate<Ret(Args...)> &other) const ENTT_NOEXCEPT { +return fn == other.fn && instance == other.instance; +} + +private: +const void *instance; +function_type *fn; +}; + +/** + * @brief Compares the contents of two delegates. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two contents differ, false otherwise. + */ +template<typename Ret, typename... Args> +[[nodiscard]] bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template<auto Candidate> +delegate(connect_arg_t<Candidate>) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate)>>>; + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + */ +template<auto Candidate, typename Type> +delegate(connect_arg_t<Candidate>, Type &&) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate), Type>>>; + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template<typename Ret, typename... Args> +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate<Ret(Args...)>; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "registry.hpp" + + +namespace entt { + +/** + * @brief Converts a registry to a view. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +struct as_view { +/*! @brief Underlying entity identifier. */ +using entity_type = std::remove_const_t<Entity>; +/*! @brief Type of registry to convert. */ +using registry_type = constness_as_t<basic_registry<entity_type>, Entity>; + +/** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ +as_view(registry_type &source) ENTT_NOEXCEPT: reg{source} {} + +/** + * @brief Conversion function from a registry to a view. + * @tparam Exclude Types of components used to filter the view. + * @tparam Component Type of components used to construct the view. + * @return A newly created view. + */ +template<typename Exclude, typename... Component> +operator basic_view<entity_type, get_t<Component...>, Exclude>() const { +return reg.template view<Component...>(Exclude{}); +} + +private: +registry_type ® +}; + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +as_view(basic_registry<Entity> &) -> as_view<Entity>; + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +as_view(const basic_registry<Entity> &) -> as_view<const Entity>; + +/** + * @brief Converts a registry to a group. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +struct as_group { +/*! @brief Underlying entity identifier. */ +using entity_type = std::remove_const_t<Entity>; +/*! @brief Type of registry to convert. */ +using registry_type = constness_as_t<basic_registry<entity_type>, Entity>; + +/** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ +as_group(registry_type &source) ENTT_NOEXCEPT: reg{source} {} + +/** + * @brief Conversion function from a registry to a group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + * @tparam Owned Types of components owned by the group. + * @return A newly created group. + */ +template<typename Get, typename Exclude, typename... Owned> +operator basic_group<entity_type, owned_t<Owned...>, Get, Exclude>() const { +if constexpr(std::is_const_v<registry_type>) { +return reg.template group_if_exists<Owned...>(Get{}, Exclude{}); +} else { +return reg.template group<Owned...>(Get{}, Exclude{}); +} +} + +private: +registry_type ® +}; + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +as_group(basic_registry<Entity> &) -> as_group<Entity>; + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +as_group(const basic_registry<Entity> &) -> as_group<const Entity>; + +/** + * @brief Helper to create a listener that directly invokes a member function. + * @tparam Member Member function to invoke on a component of the given type. + * @tparam Entity A valid entity type (see entt_traits for more details). + * @param reg A registry that contains the given entity and its components. + * @param entt Entity from which to get the component. + */ +template<auto Member, typename Entity = entity> +void invoke(basic_registry<Entity> ®, const Entity entt) { +static_assert(std::is_member_function_pointer_v<decltype(Member)>, "Invalid pointer to non-static member function"); +delegate<void(basic_registry<Entity> &, const Entity)> func; +func.template connect<Member>(reg.template get<member_class_t<decltype(Member)>>(entt)); +func(reg, entt); +} + +/** + * @brief Returns the entity associated with a given component. + * + * @warning + * Currently, this function only works correctly with the default pool as it + * makes assumptions about how the components are laid out. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Type of component. + * @param reg A registry that contains the given entity and its components. + * @param instance A valid component instance. + * @return The entity associated with the given component. + */ +template<typename Entity, typename Component> +Entity to_entity(const basic_registry<Entity> ®, const Component &instance) { +const auto &storage = reg.template storage<Component>(); +const typename basic_registry<Entity>::base_type &base = storage; +const auto *addr = std::addressof(instance); + +for(auto it = base.rbegin(), last = base.rend(); it < last; it += ENTT_PACKED_PAGE) { +if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < ENTT_PACKED_PAGE) { +return *(it + dist); +} +} + +return null; +} + +} // namespace entt + +#endif + +// #include "entity/observer.hpp" +#ifndef ENTT_ENTITY_OBSERVER_HPP +#define ENTT_ENTITY_OBSERVER_HPP + +#include <cstddef> +#include <cstdint> +#include <limits> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "../signal/delegate.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "registry.hpp" + +// #include "storage.hpp" + +// #include "utility.hpp" + + +namespace entt { + +/*! @brief Grouping matcher. */ +template<typename...> +struct matcher {}; + +/** + * @brief Collector. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template<typename...> +struct basic_collector; + +/** + * @brief Collector. + * + * A collector contains a set of rules (literally, matchers) to use to track + * entities.<br/> + * Its main purpose is to generate a descriptor that allows an observer to know + * how to connect to a registry. + */ +template<> +struct basic_collector<> { +/** + * @brief Adds a grouping matcher to the collector. + * @tparam AllOf Types of components tracked by the matcher. + * @tparam NoneOf Types of components used to filter out entities. + * @return The updated collector. + */ +template<typename... AllOf, typename... NoneOf> +static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT { +return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>>{}; +} + +/** + * @brief Adds an observing matcher to the collector. + * @tparam AnyOf Type of component for which changes should be detected. + * @return The updated collector. + */ +template<typename AnyOf> +static constexpr auto update() ENTT_NOEXCEPT { +return basic_collector<matcher<type_list<>, type_list<>, AnyOf>>{}; +} +}; + +/** + * @brief Collector. + * @copydetails basic_collector<> + * @tparam Reject Untracked types used to filter out entities. + * @tparam Require Untracked types required by the matcher. + * @tparam Rule Specific details of the current matcher. + * @tparam Other Other matchers. + */ +template<typename... Reject, typename... Require, typename... Rule, typename... Other> +struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule...>, Other...> { +/*! @brief Current matcher. */ +using current_type = matcher<type_list<Reject...>, type_list<Require...>, Rule...>; + +/** + * @brief Adds a grouping matcher to the collector. + * @tparam AllOf Types of components tracked by the matcher. + * @tparam NoneOf Types of components used to filter out entities. + * @return The updated collector. + */ +template<typename... AllOf, typename... NoneOf> +static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT { +return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>, current_type, Other...>{}; +} + +/** + * @brief Adds an observing matcher to the collector. + * @tparam AnyOf Type of component for which changes should be detected. + * @return The updated collector. + */ +template<typename AnyOf> +static constexpr auto update() ENTT_NOEXCEPT { +return basic_collector<matcher<type_list<>, type_list<>, AnyOf>, current_type, Other...>{}; +} + +/** + * @brief Updates the filter of the last added matcher. + * @tparam AllOf Types of components required by the matcher. + * @tparam NoneOf Types of components used to filter out entities. + * @return The updated collector. + */ +template<typename... AllOf, typename... NoneOf> +static constexpr auto where(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT { +using extended_type = matcher<type_list<Reject..., NoneOf...>, type_list<Require..., AllOf...>, Rule...>; +return basic_collector<extended_type, Other...>{}; +} +}; + +/*! @brief Variable template used to ease the definition of collectors. */ +inline constexpr basic_collector<> collector{}; + +/** + * @brief Observer. + * + * An observer returns all the entities and only the entities that fit the + * requirements of at least one matcher. Moreover, it's guaranteed that the + * entity list is tightly packed in memory for fast iterations.<br/> + * In general, observers don't stay true to the order of any set of components. + * + * Observers work mainly with two types of matchers, provided through a + * collector: + * + * * Observing matcher: an observer will return at least all the living entities + * for which one or more of the given components have been updated and not yet + * destroyed. + * * Grouping matcher: an observer will return at least all the living entities + * that would have entered the given group if it existed and that would have + * not yet left it. + * + * If an entity respects the requirements of multiple matchers, it will be + * returned once and only once by the observer in any case. + * + * Matchers support also filtering by means of a _where_ clause that accepts + * both a list of types and an exclusion list.<br/> + * Whenever a matcher finds that an entity matches its requirements, the + * condition of the filter is verified before to register the entity itself. + * Moreover, a registered entity isn't returned by the observer if the condition + * set by the filter is broken in the meantime. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all the other cases, modifying the pools of the given components in any + * way invalidates all the iterators and using them results in undefined + * behavior. + * + * @warning + * Lifetime of an observer doesn't necessarily have to overcome that of the + * registry to which it is connected. However, the observer must be disconnected + * from the registry before being destroyed to avoid crashes due to dangling + * pointers. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +class basic_observer { +using payload_type = std::uint32_t; + +template<typename> +struct matcher_handler; + +template<typename... Reject, typename... Require, typename AnyOf> +struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, AnyOf>> { +template<std::size_t Index> +static void maybe_valid_if(basic_observer &obs, basic_registry<Entity> ®, const Entity entt) { +if(reg.template all_of<Require...>(entt) && !reg.template any_of<Reject...>(entt)) { +if(!obs.storage.contains(entt)) { +obs.storage.emplace(entt); +} + +obs.storage.get(entt) |= (1 << Index); +} +} + +template<std::size_t Index> +static void discard_if(basic_observer &obs, basic_registry<Entity> &, const Entity entt) { +if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) { +obs.storage.erase(entt); +} +} + +template<std::size_t Index> +static void connect(basic_observer &obs, basic_registry<Entity> ®) { +(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...); +(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...); +reg.template on_update<AnyOf>().template connect<&maybe_valid_if<Index>>(obs); +reg.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(obs); +} + +static void disconnect(basic_observer &obs, basic_registry<Entity> ®) { +(reg.template on_destroy<Require>().disconnect(obs), ...); +(reg.template on_construct<Reject>().disconnect(obs), ...); +reg.template on_update<AnyOf>().disconnect(obs); +reg.template on_destroy<AnyOf>().disconnect(obs); +} +}; + +template<typename... Reject, typename... Require, typename... NoneOf, typename... AllOf> +struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, type_list<NoneOf...>, AllOf...>> { +template<std::size_t Index, typename... Ignore> +static void maybe_valid_if(basic_observer &obs, basic_registry<Entity> ®, const Entity entt) { +auto condition = [®, entt]() { +if constexpr(sizeof...(Ignore) == 0) { +return reg.template all_of<AllOf..., Require...>(entt) && !reg.template any_of<NoneOf..., Reject...>(entt); +} else { +return reg.template all_of<AllOf..., Require...>(entt) && ((std::is_same_v<Ignore..., NoneOf> || !reg.template any_of<NoneOf>(entt)) && ...) && !reg.template any_of<Reject...>(entt); +} +}; + +if(condition()) { +if(!obs.storage.contains(entt)) { +obs.storage.emplace(entt); +} + +obs.storage.get(entt) |= (1 << Index); +} +} + +template<std::size_t Index> +static void discard_if(basic_observer &obs, basic_registry<Entity> &, const Entity entt) { +if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) { +obs.storage.erase(entt); +} +} + +template<std::size_t Index> +static void connect(basic_observer &obs, basic_registry<Entity> ®) { +(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...); +(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...); +(reg.template on_construct<AllOf>().template connect<&maybe_valid_if<Index>>(obs), ...); +(reg.template on_destroy<NoneOf>().template connect<&maybe_valid_if<Index, NoneOf>>(obs), ...); +(reg.template on_destroy<AllOf>().template connect<&discard_if<Index>>(obs), ...); +(reg.template on_construct<NoneOf>().template connect<&discard_if<Index>>(obs), ...); +} + +static void disconnect(basic_observer &obs, basic_registry<Entity> ®) { +(reg.template on_destroy<Require>().disconnect(obs), ...); +(reg.template on_construct<Reject>().disconnect(obs), ...); +(reg.template on_construct<AllOf>().disconnect(obs), ...); +(reg.template on_destroy<NoneOf>().disconnect(obs), ...); +(reg.template on_destroy<AllOf>().disconnect(obs), ...); +(reg.template on_construct<NoneOf>().disconnect(obs), ...); +} +}; + +template<typename... Matcher> +static void disconnect(basic_registry<Entity> ®, basic_observer &obs) { +(matcher_handler<Matcher>::disconnect(obs, reg), ...); +} + +template<typename... Matcher, std::size_t... Index> +void connect(basic_registry<Entity> ®, std::index_sequence<Index...>) { +static_assert(sizeof...(Matcher) < std::numeric_limits<payload_type>::digits, "Too many matchers"); +(matcher_handler<Matcher>::template connect<Index>(*this, reg), ...); +release.template connect<&basic_observer::disconnect<Matcher...>>(reg); +} + +public: +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Random access iterator type. */ +using iterator = typename basic_sparse_set<Entity>::iterator; + +/*! @brief Default constructor. */ +basic_observer() +: release{}, +storage{} {} + +/*! @brief Default copy constructor, deleted on purpose. */ +basic_observer(const basic_observer &) = delete; +/*! @brief Default move constructor, deleted on purpose. */ +basic_observer(basic_observer &&) = delete; + +/** + * @brief Creates an observer and connects it to a given registry. + * @tparam Matcher Types of matchers to use to initialize the observer. + * @param reg A valid reference to a registry. + */ +template<typename... Matcher> +basic_observer(basic_registry<entity_type> ®, basic_collector<Matcher...>) +: basic_observer{} { +connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{}); +} + +/*! @brief Default destructor. */ +~basic_observer() = default; + +/** + * @brief Default copy assignment operator, deleted on purpose. + * @return This observer. + */ +basic_observer &operator=(const basic_observer &) = delete; + +/** + * @brief Default move assignment operator, deleted on purpose. + * @return This observer. + */ +basic_observer &operator=(basic_observer &&) = delete; + +/** + * @brief Connects an observer to a given registry. + * @tparam Matcher Types of matchers to use to initialize the observer. + * @param reg A valid reference to a registry. + */ +template<typename... Matcher> +void connect(basic_registry<entity_type> ®, basic_collector<Matcher...>) { +disconnect(); +connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{}); +storage.clear(); +} + +/*! @brief Disconnects an observer from the registry it keeps track of. */ +void disconnect() { +if(release) { +release(*this); +release.reset(); +} +} + +/** + * @brief Returns the number of elements in an observer. + * @return Number of elements. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return storage.size(); +} + +/** + * @brief Checks whether an observer is empty. + * @return True if the observer is empty, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return storage.empty(); +} + +/** + * @brief Direct access to the list of entities of the observer. + * + * The returned pointer is such that range `[data(), data() + size())` is + * always a valid range, even if the container is empty. + * + * @note + * Entities are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @return A pointer to the array of entities. + */ +[[nodiscard]] const entity_type *data() const ENTT_NOEXCEPT { +return storage.data(); +} + +/** + * @brief Returns an iterator to the first entity of the observer. + * + * The returned iterator points to the first entity of the observer. If the + * container is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the observer. + */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return storage.basic_sparse_set<entity_type>::begin(); +} + +/** + * @brief Returns an iterator that is past the last entity of the observer. + * + * The returned iterator points to the entity following the last entity of + * the observer. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * observer. + */ +[[nodiscard]] iterator end() const ENTT_NOEXCEPT { +return storage.basic_sparse_set<entity_type>::end(); +} + +/*! @brief Clears the underlying container. */ +void clear() ENTT_NOEXCEPT { +storage.clear(); +} + +/** + * @brief Iterates entities and applies the given function object to them. + * + * The function object is invoked for each entity.<br/> + * The signature of the function must be equivalent to the following form: + * + * @code{.cpp} + * void(const entity_type); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ +template<typename Func> +void each(Func func) const { +for(const auto entity: *this) { +func(entity); +} +} + +/** + * @brief Iterates entities and applies the given function object to them, + * then clears the observer. + * + * @sa each + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ +template<typename Func> +void each(Func func) { +std::as_const(*this).each(std::move(func)); +clear(); +} + +private: +delegate<void(basic_observer &)> release; +basic_storage<entity_type, payload_type> storage; +}; + +} // namespace entt + +#endif + +// #include "entity/organizer.hpp" +#ifndef ENTT_ENTITY_ORGANIZER_HPP +#define ENTT_ENTITY_ORGANIZER_HPP + +#include <algorithm> +#include <cstddef> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../container/dense_map.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" + +// #include "fwd.hpp" + +// #include "helper.hpp" +#ifndef ENTT_ENTITY_HELPER_HPP +#define ENTT_ENTITY_HELPER_HPP + +#include <type_traits> +// #include "../config/config.h" + +// #include "../core/fwd.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../signal/delegate.hpp" + +// #include "fwd.hpp" + +// #include "registry.hpp" + + +namespace entt { + +/** + * @brief Converts a registry to a view. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +struct as_view { +/*! @brief Underlying entity identifier. */ +using entity_type = std::remove_const_t<Entity>; +/*! @brief Type of registry to convert. */ +using registry_type = constness_as_t<basic_registry<entity_type>, Entity>; + +/** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ +as_view(registry_type &source) ENTT_NOEXCEPT: reg{source} {} + +/** + * @brief Conversion function from a registry to a view. + * @tparam Exclude Types of components used to filter the view. + * @tparam Component Type of components used to construct the view. + * @return A newly created view. + */ +template<typename Exclude, typename... Component> +operator basic_view<entity_type, get_t<Component...>, Exclude>() const { +return reg.template view<Component...>(Exclude{}); +} + +private: +registry_type ® +}; + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +as_view(basic_registry<Entity> &) -> as_view<Entity>; + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +as_view(const basic_registry<Entity> &) -> as_view<const Entity>; + +/** + * @brief Converts a registry to a group. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +struct as_group { +/*! @brief Underlying entity identifier. */ +using entity_type = std::remove_const_t<Entity>; +/*! @brief Type of registry to convert. */ +using registry_type = constness_as_t<basic_registry<entity_type>, Entity>; + +/** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ +as_group(registry_type &source) ENTT_NOEXCEPT: reg{source} {} + +/** + * @brief Conversion function from a registry to a group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + * @tparam Owned Types of components owned by the group. + * @return A newly created group. + */ +template<typename Get, typename Exclude, typename... Owned> +operator basic_group<entity_type, owned_t<Owned...>, Get, Exclude>() const { +if constexpr(std::is_const_v<registry_type>) { +return reg.template group_if_exists<Owned...>(Get{}, Exclude{}); +} else { +return reg.template group<Owned...>(Get{}, Exclude{}); +} +} + +private: +registry_type ® +}; + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +as_group(basic_registry<Entity> &) -> as_group<Entity>; + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +as_group(const basic_registry<Entity> &) -> as_group<const Entity>; + +/** + * @brief Helper to create a listener that directly invokes a member function. + * @tparam Member Member function to invoke on a component of the given type. + * @tparam Entity A valid entity type (see entt_traits for more details). + * @param reg A registry that contains the given entity and its components. + * @param entt Entity from which to get the component. + */ +template<auto Member, typename Entity = entity> +void invoke(basic_registry<Entity> ®, const Entity entt) { +static_assert(std::is_member_function_pointer_v<decltype(Member)>, "Invalid pointer to non-static member function"); +delegate<void(basic_registry<Entity> &, const Entity)> func; +func.template connect<Member>(reg.template get<member_class_t<decltype(Member)>>(entt)); +func(reg, entt); +} + +/** + * @brief Returns the entity associated with a given component. + * + * @warning + * Currently, this function only works correctly with the default pool as it + * makes assumptions about how the components are laid out. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Type of component. + * @param reg A registry that contains the given entity and its components. + * @param instance A valid component instance. + * @return The entity associated with the given component. + */ +template<typename Entity, typename Component> +Entity to_entity(const basic_registry<Entity> ®, const Component &instance) { +const auto &storage = reg.template storage<Component>(); +const typename basic_registry<Entity>::base_type &base = storage; +const auto *addr = std::addressof(instance); + +for(auto it = base.rbegin(), last = base.rend(); it < last; it += ENTT_PACKED_PAGE) { +if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < ENTT_PACKED_PAGE) { +return *(it + dist); +} +} + +return null; +} + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename> +struct is_view: std::false_type {}; + +template<typename Entity, typename... Component, typename... Exclude> +struct is_view<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>>: std::true_type {}; + +template<typename Type> +inline constexpr bool is_view_v = is_view<Type>::value; + +template<typename Type, typename Override> +struct unpack_type { +using ro = std::conditional_t< +type_list_contains_v<Override, std::add_const_t<Type>> || (std::is_const_v<Type> && !type_list_contains_v<Override, std::remove_const_t<Type>>), +type_list<std::remove_const_t<Type>>, +type_list<>>; + +using rw = std::conditional_t< +type_list_contains_v<Override, std::remove_const_t<Type>> || (!std::is_const_v<Type> && !type_list_contains_v<Override, std::add_const_t<Type>>), +type_list<Type>, +type_list<>>; +}; + +template<typename Entity, typename... Override> +struct unpack_type<basic_registry<Entity>, type_list<Override...>> { +using ro = type_list<>; +using rw = type_list<>; +}; + +template<typename Entity, typename... Override> +struct unpack_type<const basic_registry<Entity>, type_list<Override...>> +: unpack_type<basic_registry<Entity>, type_list<Override...>> {}; + +template<typename Entity, typename... Component, typename... Exclude, typename... Override> +struct unpack_type<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>> { +using ro = type_list_cat_t<type_list<Exclude...>, typename unpack_type<Component, type_list<Override...>>::ro...>; +using rw = type_list_cat_t<typename unpack_type<Component, type_list<Override...>>::rw...>; +}; + +template<typename Entity, typename... Component, typename... Exclude, typename... Override> +struct unpack_type<const basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>> +: unpack_type<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>> {}; + +template<typename, typename> +struct resource_traits; + +template<typename... Args, typename... Req> +struct resource_traits<type_list<Args...>, type_list<Req...>> { +using args = type_list<std::remove_const_t<Args>...>; +using ro = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::ro..., typename unpack_type<Req, type_list<>>::ro...>; +using rw = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::rw..., typename unpack_type<Req, type_list<>>::rw...>; +}; + +template<typename... Req, typename Ret, typename... Args> +resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource_traits(Ret (*)(Args...)); + +template<typename... Req, typename Ret, typename Type, typename... Args> +resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (*)(Type &, Args...)); + +template<typename... Req, typename Ret, typename Class, typename... Args> +resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...)); + +template<typename... Req, typename Ret, typename Class, typename... Args> +resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const); + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Utility class for creating a static task graph. + * + * This class offers minimal support (but sufficient in many cases) for creating + * an execution graph from functions and their requirements on resources.<br/> + * Note that the resulting tasks aren't executed in any case. This isn't the + * goal of the tool. Instead, they are returned to the user in the form of a + * graph that allows for safe execution. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +class basic_organizer final { +using callback_type = void(const void *, basic_registry<Entity> &); +using prepare_type = void(basic_registry<Entity> &); +using dependency_type = std::size_t(const bool, const type_info **, const std::size_t); + +struct vertex_data final { +std::size_t ro_count{}; +std::size_t rw_count{}; +const char *name{}; +const void *payload{}; +callback_type *callback{}; +dependency_type *dependency; +prepare_type *prepare{}; +const type_info *info{}; +}; + +template<typename Type> +[[nodiscard]] static decltype(auto) extract(basic_registry<Entity> ®) { +if constexpr(std::is_same_v<Type, basic_registry<Entity>>) { +return reg; +} else if constexpr(internal::is_view_v<Type>) { +return as_view{reg}; +} else { +return reg.ctx().template emplace<std::remove_reference_t<Type>>(); +} +} + +template<typename... Args> +[[nodiscard]] static auto to_args(basic_registry<Entity> ®, type_list<Args...>) { +return std::tuple<decltype(extract<Args>(reg))...>(extract<Args>(reg)...); +} + +template<typename... Type> +static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) { +if constexpr(sizeof...(Type) == 0u) { +return {}; +} else { +const type_info *info[sizeof...(Type)]{&type_id<Type>()...}; +const auto length = (std::min)(count, sizeof...(Type)); +std::copy_n(info, length, buffer); +return length; +} +} + +template<typename... RO, typename... RW> +void track_dependencies(std::size_t index, const bool requires_registry, type_list<RO...>, type_list<RW...>) { +dependencies[type_hash<basic_registry<Entity>>::value()].emplace_back(index, requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u)); +(dependencies[type_hash<RO>::value()].emplace_back(index, false), ...); +(dependencies[type_hash<RW>::value()].emplace_back(index, true), ...); +} + +[[nodiscard]] std::vector<bool> adjacency_matrix() { +const auto length = vertices.size(); +std::vector<bool> edges(length * length, false); + +// creates the adjacency matrix +for(const auto &deps: dependencies) { +const auto last = deps.second.cend(); +auto it = deps.second.cbegin(); + +while(it != last) { +if(it->second) { +// rw item +if(auto curr = it++; it != last) { +if(it->second) { +edges[curr->first * length + it->first] = true; +} else { +if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) { +for(; it != next; ++it) { +edges[curr->first * length + it->first] = true; +edges[it->first * length + next->first] = true; +} +} else { +for(; it != next; ++it) { +edges[curr->first * length + it->first] = true; +} +} +} +} +} else { +// ro item, possibly only on first iteration +if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) { +for(; it != next; ++it) { +edges[it->first * length + next->first] = true; +} +} else { +it = last; +} +} +} +} + +// computes the transitive closure +for(std::size_t vk{}; vk < length; ++vk) { +for(std::size_t vi{}; vi < length; ++vi) { +for(std::size_t vj{}; vj < length; ++vj) { +edges[vi * length + vj] = edges[vi * length + vj] || (edges[vi * length + vk] && edges[vk * length + vj]); +} +} +} + +// applies the transitive reduction +for(std::size_t vert{}; vert < length; ++vert) { +edges[vert * length + vert] = false; +} + +for(std::size_t vj{}; vj < length; ++vj) { +for(std::size_t vi{}; vi < length; ++vi) { +if(edges[vi * length + vj]) { +for(std::size_t vk{}; vk < length; ++vk) { +if(edges[vj * length + vk]) { +edges[vi * length + vk] = false; +} +} +} +} +} + +return edges; +} + +public: +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Raw task function type. */ +using function_type = callback_type; + +/*! @brief Vertex type of a task graph defined as an adjacency list. */ +struct vertex { +/** + * @brief Constructs a vertex of the task graph. + * @param vtype True if the vertex is a top-level one, false otherwise. + * @param data The data associated with the vertex. + * @param edges The indices of the children in the adjacency list. + */ +vertex(const bool vtype, vertex_data data, std::vector<std::size_t> edges) +: is_top_level{vtype}, +node{std::move(data)}, +reachable{std::move(edges)} {} + +/** + * @brief Fills a buffer with the type info objects for the writable + * resources of a vertex. + * @param buffer A buffer pre-allocated by the user. + * @param length The length of the user-supplied buffer. + * @return The number of type info objects written to the buffer. + */ +size_type ro_dependency(const type_info **buffer, const std::size_t length) const ENTT_NOEXCEPT { +return node.dependency(false, buffer, length); +} + +/** + * @brief Fills a buffer with the type info objects for the read-only + * resources of a vertex. + * @param buffer A buffer pre-allocated by the user. + * @param length The length of the user-supplied buffer. + * @return The number of type info objects written to the buffer. + */ +size_type rw_dependency(const type_info **buffer, const std::size_t length) const ENTT_NOEXCEPT { +return node.dependency(true, buffer, length); +} + +/** + * @brief Returns the number of read-only resources of a vertex. + * @return The number of read-only resources of the vertex. + */ +size_type ro_count() const ENTT_NOEXCEPT { +return node.ro_count; +} + +/** + * @brief Returns the number of writable resources of a vertex. + * @return The number of writable resources of the vertex. + */ +size_type rw_count() const ENTT_NOEXCEPT { +return node.rw_count; +} + +/** + * @brief Checks if a vertex is also a top-level one. + * @return True if the vertex is a top-level one, false otherwise. + */ +bool top_level() const ENTT_NOEXCEPT { +return is_top_level; +} + +/** + * @brief Returns a type info object associated with a vertex. + * @return A properly initialized type info object. + */ +const type_info &info() const ENTT_NOEXCEPT { +return *node.info; +} + +/** + * @brief Returns a user defined name associated with a vertex, if any. + * @return The user defined name associated with the vertex, if any. + */ +const char *name() const ENTT_NOEXCEPT { +return node.name; +} + +/** + * @brief Returns the function associated with a vertex. + * @return The function associated with the vertex. + */ +function_type *callback() const ENTT_NOEXCEPT { +return node.callback; +} + +/** + * @brief Returns the payload associated with a vertex, if any. + * @return The payload associated with the vertex, if any. + */ +const void *data() const ENTT_NOEXCEPT { +return node.payload; +} + +/** + * @brief Returns the list of nodes reachable from a given vertex. + * @return The list of nodes reachable from the vertex. + */ +const std::vector<std::size_t> &children() const ENTT_NOEXCEPT { +return reachable; +} + +/** + * @brief Prepares a registry and assures that all required resources + * are properly instantiated before using them. + * @param reg A valid registry. + */ +void prepare(basic_registry<entity_type> ®) const { +node.prepare ? node.prepare(reg) : void(); +} + +private: +bool is_top_level; +vertex_data node; +std::vector<std::size_t> reachable; +}; + +/** + * @brief Adds a free function to the task list. + * @tparam Candidate Function to add to the task list. + * @tparam Req Additional requirements and/or override resource access mode. + * @param name Optional name to associate with the task. + */ +template<auto Candidate, typename... Req> +void emplace(const char *name = nullptr) { +using resource_type = decltype(internal::free_function_to_resource_traits<Req...>(Candidate)); +constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, basic_registry<entity_type>>; + +callback_type *callback = +[](const void *, basic_registry<entity_type> ®) { +std::apply(Candidate, to_args(reg, typename resource_type::args{})); +}; + +vertex_data vdata{ +resource_type::ro::size, +resource_type::rw::size, +name, +nullptr, +callback, ++[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, ++[](basic_registry<entity_type> ®) { void(to_args(reg, typename resource_type::args{})); }, +&type_id<std::integral_constant<decltype(Candidate), Candidate>>()}; + +track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{}); +vertices.push_back(std::move(vdata)); +} + +/** + * @brief Adds a free function with payload or a member function with an + * instance to the task list. + * @tparam Candidate Function or member to add to the task list. + * @tparam Req Additional requirements and/or override resource access mode. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @param name Optional name to associate with the task. + */ +template<auto Candidate, typename... Req, typename Type> +void emplace(Type &value_or_instance, const char *name = nullptr) { +using resource_type = decltype(internal::constrained_function_to_resource_traits<Req...>(Candidate)); +constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, basic_registry<entity_type>>; + +callback_type *callback = +[](const void *payload, basic_registry<entity_type> ®) { +Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload)); +std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{}))); +}; + +vertex_data vdata{ +resource_type::ro::size, +resource_type::rw::size, +name, +&value_or_instance, +callback, ++[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, ++[](basic_registry<entity_type> ®) { void(to_args(reg, typename resource_type::args{})); }, +&type_id<std::integral_constant<decltype(Candidate), Candidate>>()}; + +track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{}); +vertices.push_back(std::move(vdata)); +} + +/** + * @brief Adds an user defined function with optional payload to the task + * list. + * @tparam Req Additional requirements and/or override resource access mode. + * @param func Function to add to the task list. + * @param payload User defined arbitrary data. + * @param name Optional name to associate with the task. + */ +template<typename... Req> +void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) { +using resource_type = internal::resource_traits<type_list<>, type_list<Req...>>; +track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{}); + +vertex_data vdata{ +resource_type::ro::size, +resource_type::rw::size, +name, +payload, +func, ++[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, +nullptr, +&type_id<void>()}; + +vertices.push_back(std::move(vdata)); +} + +/** + * @brief Generates a task graph for the current content. + * @return The adjacency list of the task graph. + */ +std::vector<vertex> graph() { +const auto edges = adjacency_matrix(); + +// creates the adjacency list +std::vector<vertex> adjacency_list{}; +adjacency_list.reserve(vertices.size()); + +for(std::size_t col{}, length = vertices.size(); col < length; ++col) { +std::vector<std::size_t> reachable{}; +const auto row = col * length; +bool is_top_level = true; + +for(std::size_t next{}; next < length; ++next) { +if(edges[row + next]) { +reachable.push_back(next); +} +} + +for(std::size_t next{}; next < length && is_top_level; ++next) { +is_top_level = !edges[next * length + col]; +} + +adjacency_list.emplace_back(is_top_level, vertices[col], std::move(reachable)); +} + +return adjacency_list; +} + +/*! @brief Erases all elements from a container. */ +void clear() { +dependencies.clear(); +vertices.clear(); +} + +private: +dense_map<id_type, std::vector<std::pair<std::size_t, bool>>, identity> dependencies; +std::vector<vertex_data> vertices; +}; + +} // namespace entt + +#endif + +// #include "entity/registry.hpp" +#ifndef ENTT_ENTITY_REGISTRY_HPP +#define ENTT_ENTITY_REGISTRY_HPP + +#include <algorithm> +#include <cstddef> +#include <iterator> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" + +// #include "../core/algorithm.hpp" + +// #include "../core/any.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" + +// #include "component.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "group.hpp" + +// #include "runtime_view.hpp" + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + +// #include "utility.hpp" + +// #include "view.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename It> +class storage_proxy_iterator final { +template<typename Other> +friend class storage_proxy_iterator; + +using mapped_type = std::remove_reference_t<decltype(std::declval<It>()->second)>; + +public: +using value_type = std::pair<id_type, constness_as_t<typename mapped_type::element_type, mapped_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; + +storage_proxy_iterator() ENTT_NOEXCEPT +: it{} {} + +storage_proxy_iterator(const It iter) ENTT_NOEXCEPT +: it{iter} {} + +template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> +storage_proxy_iterator(const storage_proxy_iterator<Other> &other) ENTT_NOEXCEPT +: it{other.it} {} + +storage_proxy_iterator &operator++() ENTT_NOEXCEPT { +return ++it, *this; +} + +storage_proxy_iterator operator++(int) ENTT_NOEXCEPT { +storage_proxy_iterator orig = *this; +return ++(*this), orig; +} + +storage_proxy_iterator &operator--() ENTT_NOEXCEPT { +return --it, *this; +} + +storage_proxy_iterator operator--(int) ENTT_NOEXCEPT { +storage_proxy_iterator orig = *this; +return operator--(), orig; +} + +storage_proxy_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { +it += value; +return *this; +} + +storage_proxy_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { +storage_proxy_iterator copy = *this; +return (copy += value); +} + +storage_proxy_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { +return (*this += -value); +} + +storage_proxy_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { +return (*this + -value); +} + +[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { +return {it[value].first, *it[value].second}; +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return {it->first, *it->second}; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +template<typename ILhs, typename IRhs> +friend std::ptrdiff_t operator-(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT; + +template<typename ILhs, typename IRhs> +friend bool operator==(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT; + +template<typename ILhs, typename IRhs> +friend bool operator<(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT; + +private: +It it; +}; + +template<typename ILhs, typename IRhs> +[[nodiscard]] std::ptrdiff_t operator-(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it - rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator==(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it == rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator!=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator<(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it < rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator>(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator<=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs > rhs); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator>=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +struct registry_context { +template<typename Type, typename... Args> +Type &emplace_hint(const id_type id, Args &&...args) { +return any_cast<Type &>(data.try_emplace(id, std::in_place_type<Type>, std::forward<Args>(args)...).first->second); +} + +template<typename Type, typename... Args> +Type &emplace(Args &&...args) { +return emplace_hint<Type>(type_id<Type>().hash(), std::forward<Args>(args)...); +} + +template<typename Type> +bool erase(const id_type id = type_id<Type>().hash()) { +const auto it = data.find(id); +return it != data.end() && it->second.type() == type_id<Type>() ? (data.erase(it), true) : false; +} + +template<typename Type> +[[nodiscard]] std::add_const_t<Type> &at(const id_type id = type_id<Type>().hash()) const { +return any_cast<std::add_const_t<Type> &>(data.at(id)); +} + +template<typename Type> +[[nodiscard]] Type &at(const id_type id = type_id<Type>().hash()) { +return any_cast<Type &>(data.at(id)); +} + +template<typename Type> +[[nodiscard]] std::add_const_t<Type> *find(const id_type id = type_id<Type>().hash()) const { +const auto it = data.find(id); +return it != data.cend() ? any_cast<std::add_const_t<Type>>(&it->second) : nullptr; +} + +template<typename Type> +[[nodiscard]] Type *find(const id_type id = type_id<Type>().hash()) { +const auto it = data.find(id); +return it != data.end() ? any_cast<Type>(&it->second) : nullptr; +} + +template<typename Type> +[[nodiscard]] bool contains(const id_type id = type_id<Type>().hash()) const { +const auto it = data.find(id); +return it != data.end() && it->second.type() == type_id<Type>(); +} + +private: +dense_map<id_type, basic_any<0u>, identity> data; +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Fast and reliable entity-component system. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +class basic_registry { +using entity_traits = entt_traits<Entity>; +using basic_common_type = basic_sparse_set<Entity>; + +template<typename Component> +using storage_type = typename storage_traits<Entity, Component>::storage_type; + +template<typename...> +struct group_handler; + +template<typename... Exclude, typename... Get, typename... Owned> +struct group_handler<exclude_t<Exclude...>, get_t<Get...>, Owned...> { +// 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<component_traits<Owned>::in_place_delete>...>, "Groups do not support in-place delete"); +std::conditional_t<sizeof...(Owned) == 0, basic_common_type, std::size_t> current{}; + +template<typename Component> +void maybe_valid_if(basic_registry &owner, const Entity entt) { +[[maybe_unused]] const auto cpools = std::forward_as_tuple(owner.assure<Owned>()...); + +const auto is_valid = ((std::is_same_v<Component, Owned> || std::get<storage_type<Owned> &>(cpools).contains(entt)) && ...) +&& ((std::is_same_v<Component, Get> || owner.assure<Get>().contains(entt)) && ...) +&& ((std::is_same_v<Component, Exclude> || !owner.assure<Exclude>().contains(entt)) && ...); + +if constexpr(sizeof...(Owned) == 0) { +if(is_valid && !current.contains(entt)) { +current.emplace(entt); +} +} else { +if(is_valid && !(std::get<0>(cpools).index(entt) < current)) { +const auto pos = current++; +(std::get<storage_type<Owned> &>(cpools).swap_elements(std::get<storage_type<Owned> &>(cpools).data()[pos], entt), ...); +} +} +} + +void discard_if([[maybe_unused]] basic_registry &owner, const Entity entt) { +if constexpr(sizeof...(Owned) == 0) { +current.remove(entt); +} else { +if(const auto cpools = std::forward_as_tuple(owner.assure<Owned>()...); std::get<0>(cpools).contains(entt) && (std::get<0>(cpools).index(entt) < current)) { +const auto pos = --current; +(std::get<storage_type<Owned> &>(cpools).swap_elements(std::get<storage_type<Owned> &>(cpools).data()[pos], entt), ...); +} +} +} +}; + +struct group_data { +std::size_t size; +std::unique_ptr<void, void (*)(void *)> group; +bool (*owned)(const id_type) ENTT_NOEXCEPT; +bool (*get)(const id_type) ENTT_NOEXCEPT; +bool (*exclude)(const id_type) ENTT_NOEXCEPT; +}; + +template<typename Component> +[[nodiscard]] auto &assure(const id_type id = type_hash<Component>::value()) { +static_assert(std::is_same_v<Component, std::decay_t<Component>>, "Non-decayed types not allowed"); +auto &&cpool = pools[id]; + +if(!cpool) { +cpool.reset(new storage_type<Component>{}); +cpool->bind(forward_as_any(*this)); +} + +ENTT_ASSERT(cpool->type() == type_id<Component>(), "Unexpected type"); +return static_cast<storage_type<Component> &>(*cpool); +} + +template<typename Component> +[[nodiscard]] const auto &assure(const id_type id = type_hash<Component>::value()) const { +static_assert(std::is_same_v<Component, std::decay_t<Component>>, "Non-decayed types not allowed"); + +if(const auto it = pools.find(id); it != pools.cend()) { +ENTT_ASSERT(it->second->type() == type_id<Component>(), "Unexpected type"); +return static_cast<const storage_type<Component> &>(*it->second); +} + +static storage_type<Component> placeholder{}; +return placeholder; +} + +auto generate_identifier(const std::size_t pos) ENTT_NOEXCEPT { +ENTT_ASSERT(pos < entity_traits::to_integral(null), "No entities available"); +return entity_traits::combine(static_cast<typename entity_traits::entity_type>(pos), {}); +} + +auto recycle_identifier() ENTT_NOEXCEPT { +ENTT_ASSERT(free_list != null, "No entities available"); +const auto curr = entity_traits::to_entity(free_list); +free_list = entity_traits::combine(entity_traits::to_integral(entities[curr]), tombstone); +return (entities[curr] = entity_traits::combine(curr, entity_traits::to_integral(entities[curr]))); +} + +auto release_entity(const Entity entity, const typename entity_traits::version_type version) { +const typename entity_traits::version_type vers = version + (version == entity_traits::to_version(tombstone)); +entities[entity_traits::to_entity(entity)] = entity_traits::construct(entity_traits::to_integral(free_list), vers); +free_list = entity_traits::combine(entity_traits::to_integral(entity), tombstone); +return vers; +} + +public: +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Underlying version type. */ +using version_type = typename entity_traits::version_type; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Common type among all storage types. */ +using base_type = basic_common_type; +/*! @brief Context type. */ +using context = internal::registry_context; + +/*! @brief Default constructor. */ +basic_registry() +: pools{}, +groups{}, +entities{}, +free_list{tombstone}, +vars{} {} + +/** + * @brief Allocates enough memory upon construction to store `count` pools. + * @param count The number of pools to allocate memory for. + */ +basic_registry(const size_type count) +: pools{}, +groups{}, +entities{}, +free_list{tombstone}, +vars{} { +pools.reserve(count); +} + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +basic_registry(basic_registry &&other) +: pools{std::move(other.pools)}, +groups{std::move(other.groups)}, +entities{std::move(other.entities)}, +free_list{other.free_list}, +vars{std::move(other.vars)} { +for(auto &&curr: pools) { +curr.second->bind(forward_as_any(*this)); +} +} + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This registry. + */ +basic_registry &operator=(basic_registry &&other) { +pools = std::move(other.pools); +groups = std::move(other.groups); +entities = std::move(other.entities); +free_list = other.free_list; +vars = std::move(other.vars); + +for(auto &&curr: pools) { +curr.second->bind(forward_as_any(*this)); +} + +return *this; +} + +/** + * @brief Returns an iterable object to use to _visit_ a registry. + * + * The iterable object returns a pair that contains the name and a reference + * to the current storage. + * + * @return An iterable object to use to _visit_ the registry. + */ +[[nodiscard]] auto storage() ENTT_NOEXCEPT { +return iterable_adaptor{internal::storage_proxy_iterator{pools.begin()}, internal::storage_proxy_iterator{pools.end()}}; +} + +/*! @copydoc storage */ +[[nodiscard]] auto storage() const ENTT_NOEXCEPT { +return iterable_adaptor{internal::storage_proxy_iterator{pools.cbegin()}, internal::storage_proxy_iterator{pools.cend()}}; +} + +/** + * @brief Finds the storage associated with a given name, if any. + * @param id Name used to map the storage within the registry. + * @return An iterator to the given storage if it's found, past the end + * iterator otherwise. + */ +[[nodiscard]] auto storage(const id_type id) { +return internal::storage_proxy_iterator{pools.find(id)}; +} + +/** + * @brief Finds the storage associated with a given name, if any. + * @param id Name used to map the storage within the registry. + * @return An iterator to the given storage if it's found, past the end + * iterator otherwise. + */ +[[nodiscard]] auto storage(const id_type id) const { +return internal::storage_proxy_iterator{pools.find(id)}; +} + +/** + * @brief Returns the storage for a given component type. + * @tparam Component Type of component of which to return the storage. + * @param id Optional name used to map the storage within the registry. + * @return The storage for the given component type. + */ +template<typename Component> +decltype(auto) storage(const id_type id = type_hash<std::remove_const_t<Component>>::value()) { +if constexpr(std::is_const_v<Component>) { +return std::as_const(*this).template storage<std::remove_const_t<Component>>(id); +} else { +return assure<Component>(id); +} +} + +/** + * @brief Returns the storage for a given component type. + * + * @warning + * If a storage for the given component doesn't exist yet, a temporary + * placeholder is returned instead. + * + * @tparam Component Type of component of which to return the storage. + * @param id Optional name used to map the storage within the registry. + * @return The storage for the given component type. + */ +template<typename Component> +decltype(auto) storage(const id_type id = type_hash<std::remove_const_t<Component>>::value()) const { +return assure<std::remove_const_t<Component>>(id); +} + +/** + * @brief Returns the number of entities created so far. + * @return Number of entities created so far. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return entities.size(); +} + +/** + * @brief Returns the number of entities still in use. + * @return Number of entities still in use. + */ +[[nodiscard]] size_type alive() const { +auto sz = entities.size(); + +for(auto curr = free_list; curr != null; --sz) { +curr = entities[entity_traits::to_entity(curr)]; +} + +return sz; +} + +/** + * @brief Increases the capacity (number of entities) of the registry. + * @param cap Desired capacity. + */ +void reserve(const size_type cap) { +entities.reserve(cap); +} + +/** + * @brief Returns the number of entities that a registry has currently + * allocated space for. + * @return Capacity of the registry. + */ +[[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { +return entities.capacity(); +} + +/** + * @brief Checks whether the registry is empty (no entities still in use). + * @return True if the registry is empty, false otherwise. + */ +[[nodiscard]] bool empty() const { +return !alive(); +} + +/** + * @brief Direct access to the list of entities of a registry. + * + * The returned pointer is such that range `[data(), data() + size())` is + * always a valid range, even if the registry is empty. + * + * @warning + * This list contains both valid and destroyed entities and isn't suitable + * for direct use. + * + * @return A pointer to the array of entities. + */ +[[nodiscard]] const entity_type *data() const ENTT_NOEXCEPT { +return entities.data(); +} + +/** + * @brief Returns the head of the list of released entities. + * + * This function is intended for use in conjunction with `assign`.<br/> + * The returned entity has an invalid identifier in all cases. + * + * @return The head of the list of released entities. + */ +[[nodiscard]] entity_type released() const ENTT_NOEXCEPT { +return free_list; +} + +/** + * @brief Checks if an identifier refers to a valid entity. + * @param entity An identifier, either valid or not. + * @return True if the identifier is valid, false otherwise. + */ +[[nodiscard]] bool valid(const entity_type entity) const { +const auto pos = size_type(entity_traits::to_entity(entity)); +return (pos < entities.size() && entities[pos] == entity); +} + +/** + * @brief Returns the actual version for an identifier. + * @param entity A valid identifier. + * @return The version for the given identifier if valid, the tombstone + * version otherwise. + */ +[[nodiscard]] version_type current(const entity_type entity) const { +const auto pos = size_type(entity_traits::to_entity(entity)); +return entity_traits::to_version(pos < entities.size() ? entities[pos] : tombstone); +} + +/** + * @brief Creates a new entity or recycles a destroyed one. + * @return A valid identifier. + */ +[[nodiscard]] entity_type create() { +return (free_list == null) ? entities.emplace_back(generate_identifier(entities.size())) : recycle_identifier(); +} + +/** + * @copybrief create + * + * If the requested entity isn't in use, the suggested identifier is used. + * Otherwise, a new identifier is generated. + * + * @param hint Required identifier. + * @return A valid identifier. + */ +[[nodiscard]] entity_type create(const entity_type hint) { +const auto length = entities.size(); + +if(hint == null || hint == tombstone) { +return create(); +} else if(const auto req = entity_traits::to_entity(hint); !(req < length)) { +entities.resize(size_type(req) + 1u, null); + +for(auto pos = length; pos < req; ++pos) { +release_entity(generate_identifier(pos), {}); +} + +return (entities[req] = hint); +} else if(const auto curr = entity_traits::to_entity(entities[req]); req == curr) { +return create(); +} else { +auto *it = &free_list; +for(; entity_traits::to_entity(*it) != req; it = &entities[entity_traits::to_entity(*it)]) {} +*it = entity_traits::combine(curr, entity_traits::to_integral(*it)); +return (entities[req] = hint); +} +} + +/** + * @brief Assigns each element in a range an identifier. + * + * @sa create + * + * @tparam It Type of 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 create(It first, It last) { +for(; free_list != null && first != last; ++first) { +*first = recycle_identifier(); +} + +const auto length = entities.size(); +entities.resize(length + std::distance(first, last), null); + +for(auto pos = length; first != last; ++first, ++pos) { +*first = entities[pos] = generate_identifier(pos); +} +} + +/** + * @brief Assigns identifiers to an empty registry. + * + * This function is intended for use in conjunction with `data`, `size` and + * `destroyed`.<br/> + * Don't try to inject ranges of randomly generated entities nor the _wrong_ + * head for the list of destroyed entities. There is no guarantee that a + * registry will continue to work properly in this case. + * + * @warning + * There must be no entities still alive for this to work properly. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param destroyed The head of the list of destroyed entities. + */ +template<typename It> +void assign(It first, It last, const entity_type destroyed) { +ENTT_ASSERT(!alive(), "Entities still alive"); +entities.assign(first, last); +free_list = destroyed; +} + +/** + * @brief Releases an identifier. + * + * The version is updated and the identifier can be recycled at any time. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @param entity A valid identifier. + * @return The version of the recycled entity. + */ +version_type release(const entity_type entity) { +return release(entity, static_cast<version_type>(entity_traits::to_version(entity) + 1u)); +} + +/** + * @brief Releases an identifier. + * + * The suggested version or the valid version closest to the suggested one + * is used instead of the implicitly generated version. + * + * @sa release + * + * @param entity A valid identifier. + * @param version A desired version upon destruction. + * @return The version actually assigned to the entity. + */ +version_type release(const entity_type entity, const version_type version) { +ENTT_ASSERT(orphan(entity), "Non-orphan entity"); +return release_entity(entity, version); +} + +/** + * @brief Releases all identifiers in a range. + * + * @sa release + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ +template<typename It> +void release(It first, It last) { +for(; first != last; ++first) { +release(*first); +} +} + +/** + * @brief Destroys an entity and releases its identifier. + * + * @sa release + * + * @warning + * Adding or removing components to an entity that is being destroyed can + * result in undefined behavior. Attempting to use an invalid entity results + * in undefined behavior. + * + * @param entity A valid identifier. + * @return The version of the recycled entity. + */ +version_type destroy(const entity_type entity) { +return destroy(entity, static_cast<version_type>(entity_traits::to_version(entity) + 1u)); +} + +/** + * @brief Destroys an entity and releases its identifier. + * + * The suggested version or the valid version closest to the suggested one + * is used instead of the implicitly generated version. + * + * @sa destroy + * + * @param entity A valid identifier. + * @param version A desired version upon destruction. + * @return The version actually assigned to the entity. + */ +version_type destroy(const entity_type entity, const version_type version) { +ENTT_ASSERT(valid(entity), "Invalid entity"); + +for(size_type pos = pools.size(); pos; --pos) { +pools.begin()[pos - 1u].second->remove(entity); +} + +return release_entity(entity, version); +} + +/** + * @brief Destroys all entities in a range and releases their identifiers. + * + * @sa destroy + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ +template<typename It> +void destroy(It first, It last) { +for(; first != last; ++first) { +destroy(*first); +} +} + +/** + * @brief Assigns the given component to an entity. + * + * The component must have a proper constructor or be of aggregate type. + * + * @warning + * Attempting to use an invalid entity or to assign a component to an entity + * that already owns it results in undefined behavior. + * + * @tparam Component Type of component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ +template<typename Component, typename... Args> +decltype(auto) emplace(const entity_type entity, Args &&...args) { +ENTT_ASSERT(valid(entity), "Invalid entity"); +return assure<Component>().emplace(entity, std::forward<Args>(args)...); +} + +/** + * @brief Assigns each entity in a range the given component. + * + * @sa emplace + * + * @tparam Component Type of component to create. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param value An instance of the component to assign. + */ +template<typename Component, typename It> +void insert(It first, It last, const Component &value = {}) { +ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); +assure<Component>().insert(first, last, value); +} + +/** + * @brief Assigns each entity in a range the given components. + * + * @sa emplace + * + * @tparam Component Type of component to create. + * @tparam EIt Type of input iterator. + * @tparam CIt Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param from An iterator to the first element of the range of components. + */ +template<typename Component, typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, Component>>> +void insert(EIt first, EIt last, CIt from) { +ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); +assure<Component>().insert(first, last, from); +} + +/** + * @brief Assigns or replaces the given component for an entity. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Type of component to assign or replace. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ +template<typename Component, typename... Args> +decltype(auto) emplace_or_replace(const entity_type entity, Args &&...args) { +ENTT_ASSERT(valid(entity), "Invalid entity"); +auto &cpool = assure<Component>(); + +return cpool.contains(entity) +? cpool.patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward<Args>(args)...}), ...); }) +: cpool.emplace(entity, std::forward<Args>(args)...); +} + +/** + * @brief Patches the given component for an entity. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(Component &); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned. However, this function can be used to trigger an update signal + * for them. + * + * @warning + * Attempting to use an invalid entity or to patch a component of an entity + * that doesn't own it results in undefined behavior. + * + * @tparam Component Type of component to patch. + * @tparam Func Types of the function objects to invoke. + * @param entity A valid identifier. + * @param func Valid function objects. + * @return A reference to the patched component. + */ +template<typename Component, typename... Func> +decltype(auto) patch(const entity_type entity, Func &&...func) { +ENTT_ASSERT(valid(entity), "Invalid entity"); +return assure<Component>().patch(entity, std::forward<Func>(func)...); +} + +/** + * @brief Replaces the given component for an entity. + * + * The component must have a proper constructor or be of aggregate type. + * + * @warning + * Attempting to use an invalid entity or to replace a component of an + * entity that doesn't own it results in undefined behavior. + * + * @tparam Component Type of component to replace. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the component being replaced. + */ +template<typename Component, typename... Args> +decltype(auto) replace(const entity_type entity, Args &&...args) { +ENTT_ASSERT(valid(entity), "Invalid entity"); +return assure<Component>().patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward<Args>(args)...}), ...); }); +} + +/** + * @brief Removes the given components from an entity. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Type of component to remove. + * @tparam Other Other types of components to remove. + * @param entity A valid identifier. + * @return The number of components actually removed. + */ +template<typename Component, typename... Other> +size_type remove(const entity_type entity) { +ENTT_ASSERT(valid(entity), "Invalid entity"); +return (assure<Component>().remove(entity) + ... + assure<Other>().remove(entity)); +} + +/** + * @brief Removes the given components from all the entities in a range. + * + * @sa remove + * + * @tparam Component Type of component to remove. + * @tparam Other Other types of components to remove. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return The number of components actually removed. + */ +template<typename Component, typename... Other, typename It> +size_type remove(It first, It last) { +if constexpr(sizeof...(Other) == 0u) { +ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); +return assure<Component>().remove(std::move(first), std::move(last)); +} else { +size_type count{}; + +for(auto cpools = std::forward_as_tuple(assure<Component>(), assure<Other>()...); first != last; ++first) { +ENTT_ASSERT(valid(*first), "Invalid entity"); +count += std::apply([entt = *first](auto &...curr) { return (curr.remove(entt) + ... + 0u); }, cpools); +} + +return count; +} +} + +/** + * @brief Erases the given components from an entity. + * + * @warning + * Attempting to use an invalid entity or to erase a component from an + * entity that doesn't own it results in undefined behavior. + * + * @tparam Component Types of components to erase. + * @tparam Other Other types of components to erase. + * @param entity A valid identifier. + */ +template<typename Component, typename... Other> +void erase(const entity_type entity) { +ENTT_ASSERT(valid(entity), "Invalid entity"); +(assure<Component>().erase(entity), (assure<Other>().erase(entity), ...)); +} + +/** + * @brief Erases the given components from all the entities in a range. + * + * @sa erase + * + * @tparam Component Types of components to erase. + * @tparam Other Other types of components to erase. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ +template<typename Component, typename... Other, typename It> +void erase(It first, It last) { +if constexpr(sizeof...(Other) == 0u) { +ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); +assure<Component>().erase(std::move(first), std::move(last)); +} else { +for(auto cpools = std::forward_as_tuple(assure<Component>(), assure<Other>()...); first != last; ++first) { +ENTT_ASSERT(valid(*first), "Invalid entity"); +std::apply([entt = *first](auto &...curr) { (curr.erase(entt), ...); }, cpools); +} +} +} + +/** + * @brief Removes all tombstones from a registry or only the pools for the + * given components. + * @tparam Component Types of components for which to clear all tombstones. + */ +template<typename... Component> +void compact() { +if constexpr(sizeof...(Component) == 0) { +for(auto &&curr: pools) { +curr.second->compact(); +} +} else { +(assure<Component>().compact(), ...); +} +} + +/** + * @brief Checks if an entity has all the given components. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Components for which to perform the check. + * @param entity A valid identifier. + * @return True if the entity has all the components, false otherwise. + */ +template<typename... Component> +[[nodiscard]] bool all_of(const entity_type entity) const { +ENTT_ASSERT(valid(entity), "Invalid entity"); +return (assure<std::remove_const_t<Component>>().contains(entity) && ...); +} + +/** + * @brief Checks if an entity has at least one of the given components. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Components for which to perform the check. + * @param entity A valid identifier. + * @return True if the entity has at least one of the given components, + * false otherwise. + */ +template<typename... Component> +[[nodiscard]] bool any_of(const entity_type entity) const { +ENTT_ASSERT(valid(entity), "Invalid entity"); +return (assure<std::remove_const_t<Component>>().contains(entity) || ...); +} + +/** + * @brief Returns references to the given components for an entity. + * + * @warning + * Attempting to use an invalid entity or to get a component from an entity + * that doesn't own it results in undefined behavior. + * + * @tparam Component Types of components to get. + * @param entity A valid identifier. + * @return References to the components owned by the entity. + */ +template<typename... Component> +[[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) const { +ENTT_ASSERT(valid(entity), "Invalid entity"); +return view<Component...>().template get<const Component...>(entity); +} + +/*! @copydoc get */ +template<typename... Component> +[[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) { +ENTT_ASSERT(valid(entity), "Invalid entity"); +return view<Component...>().template get<Component...>(entity); +} + +/** + * @brief Returns a reference to the given component for an entity. + * + * In case the entity doesn't own the component, the parameters provided are + * used to construct it. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Type of component to get. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid identifier. + * @param args Parameters to use to initialize the component. + * @return Reference to the component owned by the entity. + */ +template<typename Component, typename... Args> +[[nodiscard]] decltype(auto) get_or_emplace(const entity_type entity, Args &&...args) { +ENTT_ASSERT(valid(entity), "Invalid entity"); +auto &cpool = assure<Component>(); +return cpool.contains(entity) ? cpool.get(entity) : cpool.emplace(entity, std::forward<Args>(args)...); +} + +/** + * @brief Returns pointers to the given components for an entity. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @note + * The registry retains ownership of the pointed-to components. + * + * @tparam Component Types of components to get. + * @param entity A valid identifier. + * @return Pointers to the components owned by the entity. + */ +template<typename... Component> +[[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) const { +ENTT_ASSERT(valid(entity), "Invalid entity"); + +if constexpr(sizeof...(Component) == 1) { +const auto &cpool = assure<std::remove_const_t<Component>...>(); +return cpool.contains(entity) ? std::addressof(cpool.get(entity)) : nullptr; +} else { +return std::make_tuple(try_get<Component>(entity)...); +} +} + +/*! @copydoc try_get */ +template<typename... Component> +[[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) { +if constexpr(sizeof...(Component) == 1) { +return (const_cast<Component *>(std::as_const(*this).template try_get<Component>(entity)), ...); +} else { +return std::make_tuple(try_get<Component>(entity)...); +} +} + +/** + * @brief Clears a whole registry or the pools for the given components. + * @tparam Component Types of components to remove from their entities. + */ +template<typename... Component> +void clear() { +if constexpr(sizeof...(Component) == 0) { +for(auto &&curr: pools) { +curr.second->clear(); +} + +each([this](const auto entity) { this->release(entity); }); +} else { +(assure<Component>().clear(), ...); +} +} + +/** + * @brief Iterates all the entities that are still in use. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const Entity); + * @endcode + * + * It's not defined whether entities created during iteration are returned. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ +template<typename Func> +void each(Func func) const { +if(free_list == null) { +for(auto pos = entities.size(); pos; --pos) { +func(entities[pos - 1]); +} +} else { +for(auto pos = entities.size(); pos; --pos) { +if(const auto entity = entities[pos - 1]; entity_traits::to_entity(entity) == (pos - 1)) { +func(entity); +} +} +} +} + +/** + * @brief Checks if an entity has components assigned. + * @param entity A valid identifier. + * @return True if the entity has no components assigned, false otherwise. + */ +[[nodiscard]] bool orphan(const entity_type entity) const { +ENTT_ASSERT(valid(entity), "Invalid entity"); +return std::none_of(pools.cbegin(), pools.cend(), [entity](auto &&curr) { return curr.second->contains(entity); }); +} + +/** + * @brief Returns a sink object for the given component. + * + * Use this function to receive notifications whenever a new instance of the + * given component is created and assigned to an entity.<br/> + * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry<Entity> &, Entity); + * @endcode + * + * Listeners are invoked **after** assigning the component to the entity. + * + * @sa sink + * + * @tparam Component Type of component of which to get the sink. + * @return A temporary sink object. + */ +template<typename Component> +[[nodiscard]] auto on_construct() { +return assure<Component>().on_construct(); +} + +/** + * @brief Returns a sink object for the given component. + * + * Use this function to receive notifications whenever an instance of the + * given component is explicitly updated.<br/> + * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry<Entity> &, Entity); + * @endcode + * + * Listeners are invoked **after** updating the component. + * + * @sa sink + * + * @tparam Component Type of component of which to get the sink. + * @return A temporary sink object. + */ +template<typename Component> +[[nodiscard]] auto on_update() { +return assure<Component>().on_update(); +} + +/** + * @brief Returns a sink object for the given component. + * + * Use this function to receive notifications whenever an instance of the + * given component is removed from an entity and thus destroyed.<br/> + * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry<Entity> &, Entity); + * @endcode + * + * Listeners are invoked **before** removing the component from the entity. + * + * @sa sink + * + * @tparam Component Type of component of which to get the sink. + * @return A temporary sink object. + */ +template<typename Component> +[[nodiscard]] auto on_destroy() { +return assure<Component>().on_destroy(); +} + +/** + * @brief Returns a view for the given components. + * + * Views are created on the fly and share with the registry its internal + * data structures. Feel free to discard them after the use.<br/> + * Creating and destroying a view is an incredibly cheap operation. As a + * rule of thumb, storing a view should never be an option. + * + * @tparam Component Type of component used to construct the view. + * @tparam Other Other types of components used to construct the view. + * @tparam Exclude Types of components used to filter the view. + * @return A newly created view. + */ +template<typename Component, typename... Other, typename... Exclude> +[[nodiscard]] basic_view<entity_type, get_t<std::add_const_t<Component>, std::add_const_t<Other>...>, exclude_t<Exclude...>> view(exclude_t<Exclude...> = {}) const { +return {assure<std::remove_const_t<Component>>(), assure<std::remove_const_t<Other>>()..., assure<Exclude>()...}; +} + +/*! @copydoc view */ +template<typename Component, typename... Other, typename... Exclude> +[[nodiscard]] basic_view<entity_type, get_t<Component, Other...>, exclude_t<Exclude...>> view(exclude_t<Exclude...> = {}) { +return {assure<std::remove_const_t<Component>>(), assure<std::remove_const_t<Other>>()..., assure<Exclude>()...}; +} + +/** + * @brief Returns a group for the given components. + * + * Groups are created on the fly and share with the registry its internal + * data structures. Feel free to discard them after the use.<br/> + * Creating and destroying a group is an incredibly cheap operation. As a + * rule of thumb, storing a group should never be an option. + * + * Groups support exclusion lists and can own types of components. The more + * types are owned by a group, the faster it is to iterate entities and + * components.<br/> + * However, groups also affect some features of the registry such as the + * creation and destruction of components. + * + * @note + * Pools of components that are owned by a group cannot be sorted anymore. + * The group takes the ownership of the pools and arrange components so as + * to iterate them as fast as possible. + * + * @tparam Owned Types of components owned by the group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + * @return A newly created group. + */ +template<typename... Owned, typename... Get, typename... Exclude> +[[nodiscard]] basic_group<entity_type, owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> group(get_t<Get...>, exclude_t<Exclude...> = {}) { +static_assert(sizeof...(Owned) + sizeof...(Get) > 0, "Exclusion-only groups are not supported"); +static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1, "Single component groups are not allowed"); + +using handler_type = group_handler<exclude_t<std::remove_const_t<Exclude>...>, get_t<std::remove_const_t<Get>...>, std::remove_const_t<Owned>...>; + +const auto cpools = std::forward_as_tuple(assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()...); +constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); +handler_type *handler = nullptr; + +auto it = std::find_if(groups.cbegin(), groups.cend(), [size](const auto &gdata) { +return gdata.size == size +&& (gdata.owned(type_hash<std::remove_const_t<Owned>>::value()) && ...) +&& (gdata.get(type_hash<std::remove_const_t<Get>>::value()) && ...) +&& (gdata.exclude(type_hash<Exclude>::value()) && ...); +}); + +if(it != groups.cend()) { +handler = static_cast<handler_type *>(it->group.get()); +} else { +group_data candidate = { +size, +{new handler_type{}, [](void *instance) { delete static_cast<handler_type *>(instance); }}, +[]([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash<std::remove_const_t<Owned>>::value()) || ...); }, +[]([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash<std::remove_const_t<Get>>::value()) || ...); }, +[]([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash<Exclude>::value()) || ...); }, +}; + +handler = static_cast<handler_type *>(candidate.group.get()); + +const void *maybe_valid_if = nullptr; +const void *discard_if = nullptr; + +if constexpr(sizeof...(Owned) == 0) { +groups.push_back(std::move(candidate)); +} else { +[[maybe_unused]] auto has_conflict = [size](const auto &gdata) { +const auto overlapping = (0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value())); +const auto sz = overlapping + (0u + ... + gdata.get(type_hash<std::remove_const_t<Get>>::value())) + (0u + ... + gdata.exclude(type_hash<Exclude>::value())); +return !overlapping || ((sz == size) || (sz == gdata.size)); +}; + +ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), std::move(has_conflict)), "Conflicting groups"); + +const auto next = std::find_if_not(groups.cbegin(), groups.cend(), [size](const auto &gdata) { +return !(0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value())) || (size > gdata.size); +}); + +const auto prev = std::find_if(std::make_reverse_iterator(next), groups.crend(), [](const auto &gdata) { +return (0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value())); +}); + +maybe_valid_if = (next == groups.cend() ? maybe_valid_if : next->group.get()); +discard_if = (prev == groups.crend() ? discard_if : prev->group.get()); +groups.insert(next, std::move(candidate)); +} + +(on_construct<std::remove_const_t<Owned>>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<std::remove_const_t<Owned>>>(*handler), ...); +(on_construct<std::remove_const_t<Get>>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<std::remove_const_t<Get>>>(*handler), ...); +(on_destroy<Exclude>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<Exclude>>(*handler), ...); + +(on_destroy<std::remove_const_t<Owned>>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); +(on_destroy<std::remove_const_t<Get>>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); +(on_construct<Exclude>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); + +if constexpr(sizeof...(Owned) == 0) { +for(const auto entity: view<Owned..., Get...>(exclude<Exclude...>)) { +handler->current.emplace(entity); +} +} else { +// we cannot iterate backwards because we want to leave behind valid entities in case of owned types +for(auto *first = std::get<0>(cpools).data(), *last = first + std::get<0>(cpools).size(); first != last; ++first) { +handler->template maybe_valid_if<type_list_element_t<0, type_list<std::remove_const_t<Owned>...>>>(*this, *first); +} +} +} + +return {handler->current, std::get<storage_type<std::remove_const_t<Owned>> &>(cpools)..., std::get<storage_type<std::remove_const_t<Get>> &>(cpools)...}; +} + +/*! @copydoc group */ +template<typename... Owned, typename... Get, typename... Exclude> +[[nodiscard]] basic_group<entity_type, owned_t<std::add_const_t<Owned>...>, get_t<std::add_const_t<Get>...>, exclude_t<Exclude...>> group_if_exists(get_t<Get...>, exclude_t<Exclude...> = {}) const { +auto it = std::find_if(groups.cbegin(), groups.cend(), [](const auto &gdata) { +return gdata.size == (sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude)) +&& (gdata.owned(type_hash<std::remove_const_t<Owned>>::value()) && ...) +&& (gdata.get(type_hash<std::remove_const_t<Get>>::value()) && ...) +&& (gdata.exclude(type_hash<Exclude>::value()) && ...); +}); + +if(it == groups.cend()) { +return {}; +} else { +using handler_type = group_handler<exclude_t<std::remove_const_t<Exclude>...>, get_t<std::remove_const_t<Get>...>, std::remove_const_t<Owned>...>; +return {static_cast<handler_type *>(it->group.get())->current, assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()...}; +} +} + +/*! @copydoc group */ +template<typename... Owned, typename... Exclude> +[[nodiscard]] basic_group<entity_type, owned_t<Owned...>, get_t<>, exclude_t<Exclude...>> group(exclude_t<Exclude...> = {}) { +return group<Owned...>(get_t<>{}, exclude<Exclude...>); +} + +/*! @copydoc group */ +template<typename... Owned, typename... Exclude> +[[nodiscard]] basic_group<entity_type, owned_t<std::add_const_t<Owned>...>, get_t<>, exclude_t<Exclude...>> group_if_exists(exclude_t<Exclude...> = {}) const { +return group_if_exists<std::add_const_t<Owned>...>(get_t<>{}, exclude<Exclude...>); +} + +/** + * @brief Checks whether the given components belong to any group. + * @tparam Component Types of components in which one is interested. + * @return True if the pools of the given components are _free_, false + * otherwise. + */ +template<typename... Component> +[[nodiscard]] bool owned() const { +return std::any_of(groups.cbegin(), groups.cend(), [](auto &&gdata) { return (gdata.owned(type_hash<std::remove_const_t<Component>>::value()) || ...); }); +} + +/** + * @brief Checks whether a group can be sorted. + * @tparam Owned Types of components owned by the group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + * @return True if the group can be sorted, false otherwise. + */ +template<typename... Owned, typename... Get, typename... Exclude> +[[nodiscard]] bool sortable(const basic_group<entity_type, owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> &) ENTT_NOEXCEPT { +constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); +auto pred = [size](const auto &gdata) { return (0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value())) && (size < gdata.size); }; +return std::find_if(groups.cbegin(), groups.cend(), std::move(pred)) == groups.cend(); +} + +/** + * @brief Sorts the elements of a given component. + * + * The order remains valid until a component of the given type is assigned + * to or removed from an entity.<br/> + * The comparison function object returns `true` if the first element is + * _less_ than the second one, `false` otherwise. Its signature is also + * equivalent to one of the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * bool(const Component &, const Component &); + * @endcode + * + * Moreover, it shall induce a _strict weak ordering_ on the values.<br/> + * The sort function object offers an `operator()` that accepts: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function object to use to compare the elements. + * + * The comparison function object hasn't necessarily the type of the one + * passed along with the other parameters to this member function. + * + * @warning + * Pools of components owned by a group cannot be sorted. + * + * @tparam Component Type of components to sort. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ +template<typename Component, typename Compare, typename Sort = std_sort, typename... Args> +void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { +ENTT_ASSERT(!owned<Component>(), "Cannot sort owned storage"); +auto &cpool = assure<Component>(); + +if constexpr(std::is_invocable_v<Compare, decltype(cpool.get({})), decltype(cpool.get({}))>) { +auto comp = [&cpool, compare = std::move(compare)](const auto lhs, const auto rhs) { return compare(std::as_const(cpool.get(lhs)), std::as_const(cpool.get(rhs))); }; +cpool.sort(std::move(comp), std::move(algo), std::forward<Args>(args)...); +} else { +cpool.sort(std::move(compare), std::move(algo), std::forward<Args>(args)...); +} +} + +/** + * @brief Sorts two pools of components in the same way. + * + * Being `To` and `From` the two sets, after invoking this function an + * iterator for `To` returns elements according to the following rules: + * + * * All entities in `To` that are also in `From` are returned first + * according to the order they have in `From`. + * * All entities in `To` that are not in `From` are returned in no + * particular order after all the other entities. + * + * Any subsequent change to `From` won't affect the order in `To`. + * + * @warning + * Pools of components owned by a group cannot be sorted. + * + * @tparam To Type of components to sort. + * @tparam From Type of components to use to sort. + */ +template<typename To, typename From> +void sort() { +ENTT_ASSERT(!owned<To>(), "Cannot sort owned storage"); +assure<To>().respect(assure<From>()); +} + +/** + * @brief Returns the context object, that is, a general purpose container. + * @return The context object, that is, a general purpose container. + */ +context &ctx() ENTT_NOEXCEPT { +return vars; +} + +/*! @copydoc ctx */ +const context &ctx() const ENTT_NOEXCEPT { +return vars; +} + +private: +dense_map<id_type, std::unique_ptr<base_type>, identity> pools; +std::vector<group_data> groups; +std::vector<entity_type> entities; +entity_type free_list; +context vars; +}; + +} // namespace entt + +#endif + +// #include "entity/runtime_view.hpp" +#ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP +#define ENTT_ENTITY_RUNTIME_VIEW_HPP + +#include <algorithm> +#include <iterator> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Set> +class runtime_view_iterator final { +using iterator_type = typename Set::iterator; + +[[nodiscard]] bool valid() const { +return (!tombstone_check || *it != tombstone) +&& std::all_of(++pools->begin(), pools->end(), [entt = *it](const auto *curr) { return curr->contains(entt); }) +&& std::none_of(filter->cbegin(), filter->cend(), [entt = *it](const auto *curr) { return curr && curr->contains(entt); }); +} + +public: +using difference_type = typename iterator_type::difference_type; +using value_type = typename iterator_type::value_type; +using pointer = typename iterator_type::pointer; +using reference = typename iterator_type::reference; +using iterator_category = std::bidirectional_iterator_tag; + +runtime_view_iterator() ENTT_NOEXCEPT +: pools{}, +filter{}, +it{}, +tombstone_check{} {} + +runtime_view_iterator(const std::vector<const Set *> &cpools, const std::vector<const Set *> &ignore, iterator_type curr) ENTT_NOEXCEPT +: pools{&cpools}, +filter{&ignore}, +it{curr}, +tombstone_check{pools->size() == 1u && (*pools)[0u]->policy() == deletion_policy::in_place} { +if(it != (*pools)[0]->end() && !valid()) { +++(*this); +} +} + +runtime_view_iterator &operator++() { +while(++it != (*pools)[0]->end() && !valid()) {} +return *this; +} + +runtime_view_iterator operator++(int) { +runtime_view_iterator orig = *this; +return ++(*this), orig; +} + +runtime_view_iterator &operator--() { +while(--it != (*pools)[0]->begin() && !valid()) {} +return *this; +} + +runtime_view_iterator operator--(int) { +runtime_view_iterator orig = *this; +return operator--(), orig; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return it.operator->(); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return *operator->(); +} + +[[nodiscard]] bool operator==(const runtime_view_iterator &other) const ENTT_NOEXCEPT { +return it == other.it; +} + +[[nodiscard]] bool operator!=(const runtime_view_iterator &other) const ENTT_NOEXCEPT { +return !(*this == other); +} + +private: +const std::vector<const Set *> *pools; +const std::vector<const Set *> *filter; +iterator_type it; +bool tombstone_check; +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Runtime view implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template<typename> +struct basic_runtime_view; + +/** + * @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. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all the other cases, modifying the pools of the given components in any + * way invalidates all the iterators and using them results in undefined + * behavior. + * + * @note + * Views share references to the underlying data structures of the registry that + * generated them. Therefore any change to the entities and to the components + * made by means of the registry are immediately reflected by the views, unless + * a pool was missing when the view was built (in this case, the view won't + * have a valid reference and won't be updated accordingly). + * + * @warning + * Lifetime of a view must not overcome that of the registry that generated it. + * In any other case, attempting to use a view results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Entity, typename Allocator> +struct basic_runtime_view<basic_sparse_set<Entity, Allocator>> { +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Common type among all storage types. */ +using base_type = basic_sparse_set<Entity, Allocator>; +/*! @brief Bidirectional iterator type. */ +using iterator = internal::runtime_view_iterator<base_type>; + +/*! @brief Default constructor to use to create empty, invalid views. */ +basic_runtime_view() ENTT_NOEXCEPT +: pools{}, +filter{} {} + +/** + * @brief Appends an opaque storage object to a runtime view. + * @param base An opaque reference to a storage object. + * @return This runtime view. + */ +basic_runtime_view &iterate(const base_type &base) { +if(pools.empty() || !(base.size() < pools[0u]->size())) { +pools.push_back(&base); +} else { +pools.push_back(std::exchange(pools[0u], &base)); +} + +return *this; +} + +/** + * @brief Adds an opaque storage object as a filter of a runtime view. + * @param base An opaque reference to a storage object. + * @return This runtime view. + */ +basic_runtime_view &exclude(const base_type &base) { +filter.push_back(&base); +return *this; +} + +/** + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. + */ +[[nodiscard]] size_type size_hint() const { +return pools.empty() ? size_type{} : pools.front()->size(); +} + +/** + * @brief Returns an iterator to the first entity that has the given + * components. + * + * The returned iterator points to the first entity that has the given + * components. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @return An iterator to the first entity that has the given components. + */ +[[nodiscard]] iterator begin() const { +return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->begin()}; +} + +/** + * @brief Returns an iterator that is past the last entity that has the + * given components. + * + * The returned iterator points to the entity following the last entity that + * has the given components. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the entity following the last entity that has the + * given components. + */ +[[nodiscard]] iterator end() const { +return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()}; +} + +/** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ +[[nodiscard]] bool contains(const entity_type entt) const { +return !pools.empty() +&& std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); }) +&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); }); +} + +/** + * @brief Iterates entities and applies the given function object to them. + * + * The function object is invoked for each entity. It is provided only with + * the entity itself. To get the components, users can use the registry with + * which the view was built.<br/> + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const entity_type); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ +template<typename Func> +void each(Func func) const { +for(const auto entity: *this) { +func(entity); +} +} + +private: +std::vector<const base_type *> pools; +std::vector<const base_type *> filter; +}; + +} // namespace entt + +#endif + +// #include "entity/sigh_storage_mixin.hpp" +#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_iterator = typename Type::basic_iterator; + +template<typename Func> +void notify_destruction(basic_iterator first, basic_iterator last, Func func) { +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); +func(it, it + 1u); +} +} + +void swap_and_pop(basic_iterator first, basic_iterator last) final { +notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::swap_and_pop(args...); }); +} + +void in_place_pop(basic_iterator first, basic_iterator last) final { +notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::in_place_pop(args...); }); +} + +basic_iterator try_emplace(const typename 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 Underlying entity identifier. */ +using entity_type = typename Type::entity_type; + +/*! @brief Inherited constructors. */ +using Type::Type; + +/** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever a new instance is created and assigned to an entity.<br/> + * Listeners are invoked after the object has been assigned to the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ +[[nodiscard]] auto on_construct() ENTT_NOEXCEPT { +return sink{construction}; +} + +/** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is explicitly updated.<br/> + * Listeners are invoked after the object has been updated. + * + * @sa sink + * + * @return A temporary sink object. + */ +[[nodiscard]] auto on_update() ENTT_NOEXCEPT { +return sink{update}; +} + +/** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is removed from an entity and thus destroyed.<br/> + * Listeners are invoked before the object has been removed from the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ +[[nodiscard]] auto on_destroy() ENTT_NOEXCEPT { +return sink{destruction}; +} + +/** + * @brief Assigns entities to a storage. + * @tparam Args Types of arguments to use to construct the object. + * @param 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 mixins, if any. + * @param value A variable wrapped in an opaque container. + */ +void bind(any value) ENTT_NOEXCEPT final { +auto *reg = any_cast<basic_registry<entity_type>>(&value); +owner = reg ? reg : owner; +Type::bind(std::move(value)); +} + +private: +sigh<void(basic_registry<entity_type> &, const entity_type)> construction{}; +sigh<void(basic_registry<entity_type> &, const entity_type)> destruction{}; +sigh<void(basic_registry<entity_type> &, const entity_type)> update{}; +basic_registry<entity_type> *owner{}; +}; + +} // namespace entt + +#endif + +// #include "entity/snapshot.hpp" +#ifndef ENTT_ENTITY_SNAPSHOT_HPP +#define ENTT_ENTITY_SNAPSHOT_HPP + +#include <array> +#include <cstddef> +#include <iterator> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" + +// #include "../core/type_traits.hpp" + +// #include "component.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "registry.hpp" + + +namespace entt { + +/** + * @brief Utility class to create snapshots from a registry. + * + * A _snapshot_ can be either a dump of the entire registry or a narrower + * selection of components of interest.<br/> + * This type can be used in both cases if provided with a correctly configured + * output archive. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +class basic_snapshot { +using entity_traits = entt_traits<Entity>; + +template<typename Component, typename Archive, typename It> +void get(Archive &archive, std::size_t sz, It first, It last) const { +const auto view = reg->template view<std::add_const_t<Component>>(); +archive(typename 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), ...); +} + +public: +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; + +/** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ +basic_snapshot(const basic_registry<entity_type> &source) ENTT_NOEXCEPT +: reg{&source} {} + +/*! @brief Default move constructor. */ +basic_snapshot(basic_snapshot &&) ENTT_NOEXCEPT = default; + +/*! @brief Default move assignment operator. @return This snapshot. */ +basic_snapshot &operator=(basic_snapshot &&) ENTT_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. + * + * @tparam Archive Type of output archive. + * @param archive A valid reference to an output archive. + * @return An object of this type to continue creating the snapshot. + */ +template<typename Archive> +const basic_snapshot &entities(Archive &archive) const { +const auto sz = reg->size(); + +archive(typename entity_traits::entity_type(sz + 1u)); +archive(reg->released()); + +for(auto first = reg->data(), last = first + sz; first != last; ++first) { +archive(*first); +} + +return *this; +} + +/** + * @brief Puts aside the given components. + * + * Each instance is serialized together with the entity to which it belongs. + * Entities are serialized along with their versions. + * + * @tparam Component Types of components to serialize. + * @tparam Archive Type of output archive. + * @param archive A valid reference to an output archive. + * @return An object of this type to continue creating the snapshot. + */ +template<typename... Component, typename Archive> +const basic_snapshot &component(Archive &archive) const { +if constexpr(sizeof...(Component) == 1u) { +const auto view = reg->template view<const Component...>(); +(component<Component>(archive, view.rbegin(), view.rend()), ...); +return *this; +} else { +(component<Component>(archive), ...); +return *this; +} +} + +/** + * @brief Puts aside the given components for the entities in a range. + * + * Each instance is serialized together with the entity to which it belongs. + * Entities are serialized along with their versions. + * + * @tparam Component Types of components to serialize. + * @tparam Archive Type of output archive. + * @tparam It Type of input iterator. + * @param archive A valid reference to an output archive. + * @param first An iterator to the first element of the range to serialize. + * @param last An iterator past the last element of the range to serialize. + * @return An object of this type to continue creating the snapshot. + */ +template<typename... Component, typename Archive, typename It> +const basic_snapshot &component(Archive &archive, It first, It last) const { +component<Component...>(archive, first, last, std::index_sequence_for<Component...>{}); +return *this; +} + +private: +const basic_registry<entity_type> *reg; +}; + +/** + * @brief Utility class to restore a snapshot as a whole. + * + * A snapshot loader requires that the destination registry be empty and loads + * all the data at once while keeping intact the identifiers that the entities + * originally had.<br/> + * An example of use is the implementation of a save/restore utility. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +class basic_snapshot_loader { +using entity_traits = entt_traits<Entity>; + +template<typename Type, typename Archive> +void assign(Archive &archive) const { +typename entity_traits::entity_type length{}; +entity_type entt; + +archive(length); + +if constexpr(ignore_as_empty_v<Type>) { +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<Type>(entt); +} +} else { +Type 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<Type>(entt, std::move(instance)); +} +} +} + +public: +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; + +/** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ +basic_snapshot_loader(basic_registry<entity_type> &source) ENTT_NOEXCEPT +: reg{&source} { +// restoring a snapshot as a whole requires a clean registry +ENTT_ASSERT(reg->empty(), "Registry must be empty"); +} + +/*! @brief Default move constructor. */ +basic_snapshot_loader(basic_snapshot_loader &&) ENTT_NOEXCEPT = default; + +/*! @brief Default move assignment operator. @return This loader. */ +basic_snapshot_loader &operator=(basic_snapshot_loader &&) ENTT_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. + * + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @return A valid loader to continue restoring data. + */ +template<typename Archive> +const basic_snapshot_loader &entities(Archive &archive) const { +typename entity_traits::entity_type length{}; + +archive(length); +std::vector<entity_type> all(length); + +for(std::size_t pos{}; pos < length; ++pos) { +archive(all[pos]); +} + +reg->assign(++all.cbegin(), all.cend(), all[0u]); + +return *this; +} + +/** + * @brief Restores components and assigns them to the right entities. + * + * The template parameter list must be exactly the same used during + * serialization. In the event that the entity to which the component is + * assigned doesn't exist yet, the loader will take care to create it with + * the version it originally had. + * + * @tparam Component Types of components to restore. + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @return A valid loader to continue restoring data. + */ +template<typename... Component, typename Archive> +const basic_snapshot_loader &component(Archive &archive) const { +(assign<Component>(archive), ...); +return *this; +} + +/** + * @brief Destroys those entities that have no components. + * + * In case all the entities were serialized but only part of the components + * was saved, it could happen that some of the entities have no components + * once restored.<br/> + * This functions helps to identify and destroy those entities. + * + * @return A valid loader to continue restoring data. + */ +const basic_snapshot_loader &orphans() const { +reg->each([this](const auto entt) { +if(reg->orphan(entt)) { +reg->release(entt); +} +}); + +return *this; +} + +private: +basic_registry<entity_type> *reg; +}; + +/** + * @brief Utility class for _continuous loading_. + * + * A _continuous loader_ is designed to load data from a source registry to a + * (possibly) non-empty destination. The loader can accommodate in a registry + * more than one snapshot in a sort of _continuous loading_ that updates the + * destination one step at a time.<br/> + * Identifiers that entities originally had are not transferred to the target. + * Instead, the loader maps remote identifiers to local ones while restoring a + * snapshot.<br/> + * An example of use is the implementation of a client-server applications with + * the requirement of transferring somehow parts of the representation side to + * side. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template<typename Entity> +class basic_continuous_loader { +using entity_traits = entt_traits<Entity>; + +void destroy(Entity entt) { +if(const auto it = remloc.find(entt); it == remloc.cend()) { +const auto local = reg->create(); +remloc.emplace(entt, std::make_pair(local, true)); +reg->destroy(local); +} +} + +void restore(Entity entt) { +const auto it = remloc.find(entt); + +if(it == remloc.cend()) { +const auto local = reg->create(); +remloc.emplace(entt, std::make_pair(local, true)); +} else { +if(!reg->valid(remloc[entt].first)) { +remloc[entt].first = reg->create(); +} + +// set the dirty flag +remloc[entt].second = true; +} +} + +template<typename Container> +auto update(int, Container &container) -> decltype(typename Container::mapped_type{}, void()) { +// map like container +Container other; + +for(auto &&pair: container) { +using first_type = std::remove_const_t<typename std::decay_t<decltype(pair)>::first_type>; +using second_type = typename std::decay_t<decltype(pair)>::second_type; + +if constexpr(std::is_same_v<first_type, entity_type> && std::is_same_v<second_type, entity_type>) { +other.emplace(map(pair.first), map(pair.second)); +} else if constexpr(std::is_same_v<first_type, entity_type>) { +other.emplace(map(pair.first), std::move(pair.second)); +} else { +static_assert(std::is_same_v<second_type, entity_type>, "Neither the key nor the value are of entity type"); +other.emplace(std::move(pair.first), map(pair.second)); +} +} + +using std::swap; +swap(container, other); +} + +template<typename Container> +auto update(char, Container &container) -> decltype(typename Container::value_type{}, void()) { +// vector like container +static_assert(std::is_same_v<typename Container::value_type, entity_type>, "Invalid value type"); + +for(auto &&entt: container) { +entt = map(entt); +} +} + +template<typename Other, typename Type, typename Member> +void update([[maybe_unused]] Other &instance, [[maybe_unused]] Member Type::*member) { +if constexpr(!std::is_same_v<Other, Type>) { +return; +} else if constexpr(std::is_same_v<Member, entity_type>) { +instance.*member = map(instance.*member); +} else { +// maybe a container? let's try... +update(0, instance.*member); +} +} + +template<typename Component> +void remove_if_exists() { +for(auto &&ref: remloc) { +const auto local = ref.second.first; + +if(reg->valid(local)) { +reg->template remove<Component>(local); +} +} +} + +template<typename Other, typename Archive, typename... Type, typename... Member> +void assign(Archive &archive, [[maybe_unused]] Member Type::*...member) { +typename entity_traits::entity_type length{}; +entity_type entt; + +archive(length); + +if constexpr(ignore_as_empty_v<Other>) { +while(length--) { +archive(entt); +restore(entt); +reg->template emplace_or_replace<Other>(map(entt)); +} +} else { +Other instance; + +while(length--) { +archive(entt, instance); +(update(instance, member), ...); +restore(entt); +reg->template emplace_or_replace<Other>(map(entt), std::move(instance)); +} +} +} + +public: +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; + +/** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ +basic_continuous_loader(basic_registry<entity_type> &source) ENTT_NOEXCEPT +: reg{&source} {} + +/*! @brief Default move constructor. */ +basic_continuous_loader(basic_continuous_loader &&) = default; + +/*! @brief Default move assignment operator. @return This loader. */ +basic_continuous_loader &operator=(basic_continuous_loader &&) = default; + +/** + * @brief Restores entities that were in use during serialization. + * + * This function restores the entities that were in use during serialization + * and creates local counterparts for them if required. + * + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @return A non-const reference to this loader. + */ +template<typename Archive> +basic_continuous_loader &entities(Archive &archive) { +typename 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; +} + +/** + * @brief Restores components and assigns them to the right entities. + * + * The template parameter list must be exactly the same used during + * serialization. In the event that the entity to which the component is + * assigned doesn't exist yet, the loader will take care to create a local + * counterpart for it.<br/> + * Members can be either data members of type entity_type or containers of + * entities. In both cases, the loader will visit them and update the + * entities by replacing each one with its local counterpart. + * + * @tparam Component Type of component to restore. + * @tparam Archive Type of input archive. + * @tparam Type Types of components to update with local counterparts. + * @tparam Member Types of members to update with their local counterparts. + * @param archive A valid reference to an input archive. + * @param member Members to update with their local counterparts. + * @return A non-const reference to this loader. + */ +template<typename... Component, typename Archive, typename... Type, typename... Member> +basic_continuous_loader &component(Archive &archive, Member Type::*...member) { +(remove_if_exists<Component>(), ...); +(assign<Component>(archive, member...), ...); +return *this; +} + +/** + * @brief Helps to purge entities that no longer have a conterpart. + * + * Users should invoke this member function after restoring each snapshot, + * unless they know exactly what they are doing. + * + * @return A non-const reference to this loader. + */ +basic_continuous_loader &shrink() { +auto it = remloc.begin(); + +while(it != remloc.cend()) { +const auto local = it->second.first; +bool &dirty = it->second.second; + +if(dirty) { +dirty = false; +++it; +} else { +if(reg->valid(local)) { +reg->destroy(local); +} + +it = remloc.erase(it); +} +} + +return *this; +} + +/** + * @brief Destroys those entities that have no components. + * + * In case all the entities were serialized but only part of the components + * was saved, it could happen that some of the entities have no components + * once restored.<br/> + * This functions helps to identify and destroy those entities. + * + * @return A non-const reference to this loader. + */ +basic_continuous_loader &orphans() { +reg->each([this](const auto entt) { +if(reg->orphan(entt)) { +reg->release(entt); +} +}); + +return *this; +} + +/** + * @brief Tests if a loader knows about a given entity. + * @param entt A valid identifier. + * @return True if `entity` is managed by the loader, false otherwise. + */ +[[nodiscard]] bool contains(entity_type entt) const ENTT_NOEXCEPT { +return (remloc.find(entt) != remloc.cend()); +} + +/** + * @brief Returns the identifier to which an entity refers. + * @param entt A valid identifier. + * @return The local identifier if any, the null entity otherwise. + */ +[[nodiscard]] entity_type map(entity_type entt) const ENTT_NOEXCEPT { +const auto it = remloc.find(entt); +entity_type other = null; + +if(it != remloc.cend()) { +other = it->second.first; +} + +return other; +} + +private: +dense_map<entity_type, std::pair<entity_type, bool>> remloc; +basic_registry<entity_type> *reg; +}; + +} // namespace entt + +#endif + +// #include "entity/sparse_set.hpp" +#ifndef ENTT_ENTITY_SPARSE_SET_HPP +#define ENTT_ENTITY_SPARSE_SET_HPP + +#include <cstddef> +#include <iterator> +#include <memory> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" + +// #include "../core/algorithm.hpp" + +// #include "../core/any.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_info.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Container> +struct sparse_set_iterator final { +using value_type = typename Container::value_type; +using pointer = typename Container::const_pointer; +using reference = typename Container::const_reference; +using difference_type = typename Container::difference_type; +using iterator_category = std::random_access_iterator_tag; + +sparse_set_iterator() ENTT_NOEXCEPT +: packed{}, +offset{} {} + +sparse_set_iterator(const Container &ref, const difference_type idx) ENTT_NOEXCEPT +: packed{std::addressof(ref)}, +offset{idx} {} + +sparse_set_iterator &operator++() ENTT_NOEXCEPT { +return --offset, *this; +} + +sparse_set_iterator operator++(int) ENTT_NOEXCEPT { +sparse_set_iterator orig = *this; +return ++(*this), orig; +} + +sparse_set_iterator &operator--() ENTT_NOEXCEPT { +return ++offset, *this; +} + +sparse_set_iterator operator--(int) ENTT_NOEXCEPT { +sparse_set_iterator orig = *this; +return operator--(), orig; +} + +sparse_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { +offset -= value; +return *this; +} + +sparse_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { +sparse_set_iterator copy = *this; +return (copy += value); +} + +sparse_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { +return (*this += -value); +} + +sparse_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { +return (*this + -value); +} + +[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { +return packed->data()[index() - value]; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return packed->data() + index(); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return *operator->(); +} + +[[nodiscard]] difference_type index() const ENTT_NOEXCEPT { +return offset - 1; +} + +private: +const Container *packed; +difference_type offset; +}; + +template<typename Type, typename Other> +[[nodiscard]] std::ptrdiff_t operator-(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT { +return rhs.index() - lhs.index(); +} + +template<typename Type, typename Other> +[[nodiscard]] bool operator==(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT { +return lhs.index() == rhs.index(); +} + +template<typename Type, typename Other> +[[nodiscard]] bool operator!=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +template<typename Type, typename Other> +[[nodiscard]] bool operator<(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT { +return lhs.index() > rhs.index(); +} + +template<typename Type, typename Other> +[[nodiscard]] bool operator>(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT { +return lhs.index() < rhs.index(); +} + +template<typename Type, typename Other> +[[nodiscard]] bool operator<=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT { +return !(lhs > rhs); +} + +template<typename Type, typename Other> +[[nodiscard]] bool operator>=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @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. + * + * Sparse set or packed array or whatever is the name users give it.<br/> + * 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. + * + * @note + * Internal data structures arrange elements to maximize performance. There are + * no guarantees that entities are returned in the insertion order when iterate + * a sparse set. Do not make assumption on the order in any case. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Entity, typename Allocator> +class basic_sparse_set { +using alloc_traits = std::allocator_traits<Allocator>; +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; +} + +[[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)]; +} + +[[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; + +if(!(page < sparse.size())) { +sparse.resize(page + 1u, nullptr); +} + +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); +} + +auto &elem = sparse[page][fast_mod(pos, entity_traits::page_size)]; +ENTT_ASSERT(entity_traits::to_version(elem) == entity_traits::to_version(tombstone), "Slot not available"); +return elem; +} + +void release_sparse_pages() { +auto page_allocator{packed.get_allocator()}; + +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); +page = nullptr; +} +} +} + +private: +virtual const void *get_at(const std::size_t) const { +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) {} + +protected: +/*! @brief Random access iterator type. */ +using basic_iterator = internal::sparse_set_iterator<packed_container_type>; + +/** + * @brief Erases entities from a sparse set. + * @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. + */ +virtual void swap_and_pop(basic_iterator first, basic_iterator last) { +for(; first != last; ++first) { +sparse_ref(packed.back()) = entity_traits::combine(static_cast<typename entity_traits::entity_type>(first.index()), entity_traits::to_integral(packed.back())); +const auto entt = std::exchange(packed[first.index()], packed.back()); +// unnecessary but it helps to detect nasty bugs +ENTT_ASSERT((packed.back() = tombstone, true), ""); +// lazy self-assignment guard +sparse_ref(entt) = null; +packed.pop_back(); +} +} + +/** + * @brief Erases entities from a sparse set. + * @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. + */ +virtual void in_place_pop(basic_iterator first, basic_iterator last) { +for(; first != last; ++first) { +sparse_ref(*first) = null; +packed[first.index()] = std::exchange(free_list, entity_traits::combine(static_cast<typename entity_traits::entity_type>(first.index()), entity_traits::reserved)); +} +} + +/** + * @brief Assigns an entity to a sparse set. + * @param entt A valid identifier. + * @param force_back Force back insertion. + * @return Iterator pointing to the emplaced element. + */ +virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) { +ENTT_ASSERT(!contains(entt), "Set already contains entity"); + +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)); +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)); +free_list = std::exchange(packed[pos], entt); +return --(end() - pos); +} +} + +public: +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Underlying version type. */ +using version_type = typename entity_traits::version_type; +/*! @brief Unsigned integer type. */ +using size_type = typename packed_container_type::size_type; +/*! @brief Pointer type to contained entities. */ +using pointer = typename packed_container_type::const_pointer; +/*! @brief Random access iterator type. */ +using iterator = basic_iterator; +/*! @brief Constant random access iterator type. */ +using const_iterator = iterator; +/*! @brief Reverse iterator type. */ +using reverse_iterator = std::reverse_iterator<iterator>; +/*! @brief Constant reverse iterator type. */ +using const_reverse_iterator = reverse_iterator; + +/*! @brief Default constructor. */ +basic_sparse_set() +: basic_sparse_set{type_id<void>()} {} + +/** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ +explicit basic_sparse_set(const allocator_type &allocator) +: basic_sparse_set{type_id<void>(), deletion_policy::swap_and_pop, allocator} {} + +/** + * @brief Constructs an empty container with the given policy and allocator. + * @param pol Type of deletion policy. + * @param allocator The allocator to use (possibly default-constructed). + */ +explicit basic_sparse_set(deletion_policy pol, const allocator_type &allocator = {}) +: basic_sparse_set{type_id<void>(), pol, allocator} {} + +/** + * @brief Constructs an empty container with the given value type, policy + * and allocator. + * @param value 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 = {}) +: sparse{allocator}, +packed{allocator}, +info{&value}, +free_list{tombstone}, +mode{pol} {} + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +basic_sparse_set(basic_sparse_set &&other) ENTT_NOEXCEPT +: sparse{std::move(other.sparse)}, +packed{std::move(other.packed)}, +info{other.info}, +free_list{std::exchange(other.free_list, tombstone)}, +mode{other.mode} {} + +/** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ +basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) ENTT_NOEXCEPT +: sparse{std::move(other.sparse), allocator}, +packed{std::move(other.packed), allocator}, +info{other.info}, +free_list{std::exchange(other.free_list, tombstone)}, +mode{other.mode} { +ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed"); +} + +/*! @brief Default destructor. */ +virtual ~basic_sparse_set() { +release_sparse_pages(); +} + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This sparse set. + */ +basic_sparse_set &operator=(basic_sparse_set &&other) ENTT_NOEXCEPT { +ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed"); + +release_sparse_pages(); +sparse = std::move(other.sparse); +packed = std::move(other.packed); +info = other.info; +free_list = std::exchange(other.free_list, tombstone); +mode = other.mode; +return *this; +} + +/** + * @brief Exchanges the contents with those of a given sparse set. + * @param other Sparse set to exchange the content with. + */ +void swap(basic_sparse_set &other) { +using std::swap; +swap(sparse, other.sparse); +swap(packed, other.packed); +swap(info, other.info); +swap(free_list, other.free_list); +swap(mode, other.mode); +} + +/** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ +[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { +return packed.get_allocator(); +} + +/** + * @brief Returns the deletion policy of a sparse set. + * @return The deletion policy of the sparse set. + */ +[[nodiscard]] deletion_policy policy() const ENTT_NOEXCEPT { +return mode; +} + +/** + * @brief Increases the capacity of a sparse set. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ +virtual void reserve(const size_type cap) { +packed.reserve(cap); +} + +/** + * @brief Returns the number of elements that a sparse set has currently + * allocated space for. + * @return Capacity of the sparse set. + */ +[[nodiscard]] virtual size_type capacity() const ENTT_NOEXCEPT { +return packed.capacity(); +} + +/*! @brief Requests the removal of unused capacity. */ +virtual void shrink_to_fit() { +packed.shrink_to_fit(); +} + +/** + * @brief Returns the extent of a sparse set. + * + * The extent of a sparse set is also the size of the internal sparse array. + * There is no guarantee that the internal packed array has the same size. + * Usually the size of the internal sparse array is equal or greater than + * the one of the internal packed array. + * + * @return Extent of the sparse set. + */ +[[nodiscard]] size_type extent() const ENTT_NOEXCEPT { +return sparse.size() * entity_traits::page_size; +} + +/** + * @brief Returns the number of elements in a sparse set. + * + * The number of elements is also the size of the internal packed array. + * There is no guarantee that the internal sparse array has the same size. + * Usually the size of the internal sparse array is equal or greater than + * the one of the internal packed array. + * + * @return Number of elements. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return packed.size(); +} + +/** + * @brief Checks whether a sparse set is empty. + * @return True if the sparse set is empty, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return packed.empty(); +} + +/** + * @brief Direct access to the internal packed array. + * @return A pointer to the internal packed array. + */ +[[nodiscard]] pointer data() const ENTT_NOEXCEPT { +return packed.data(); +} + +/** + * @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 + * `end()`. + * + * @return An iterator to the first entity of the sparse set. + */ +[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { +const auto pos = static_cast<typename iterator::difference_type>(packed.size()); +return iterator{packed, pos}; +} + +/*! @copydoc begin */ +[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { +return begin(); +} + +/** + * @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. + */ +[[nodiscard]] iterator end() const ENTT_NOEXCEPT { +return iterator{packed, {}}; +} + +/*! @copydoc end */ +[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { +return end(); +} + +/** + * @brief Returns a reverse iterator to the beginning. + * + * The returned iterator points to the first entity of the reversed internal + * packed array. If the sparse set is empty, the returned iterator will be + * equal to `rend()`. + * + * @return An iterator to the first entity of the reversed internal packed + * array. + */ +[[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT { +return std::make_reverse_iterator(end()); +} + +/*! @copydoc rbegin */ +[[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT { +return rbegin(); +} + +/** + * @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. + */ +[[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { +return std::make_reverse_iterator(begin()); +} + +/*! @copydoc rend */ +[[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT { +return rend(); +} + +/** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ +[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT { +return contains(entt) ? --(end() - index(entt)) : end(); +} + +/** + * @brief Checks if a sparse set contains an entity. + * @param entt A valid identifier. + * @return True if the sparse set contains the entity, false otherwise. + */ +[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { +const auto elem = sparse_ptr(entt); +constexpr auto cap = entity_traits::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); +} + +/** + * @brief Returns the contained version for an identifier. + * @param entt A valid identifier. + * @return The version for the given identifier if present, the tombstone + * version otherwise. + */ +[[nodiscard]] version_type current(const entity_type entt) const ENTT_NOEXCEPT { +const auto elem = sparse_ptr(entt); +constexpr auto fallback = entity_traits::to_version(tombstone); +return elem ? entity_traits::to_version(*elem) : fallback; +} + +/** + * @brief Returns the position of an entity in a sparse set. + * + * @warning + * Attempting to get the position of an entity that doesn't belong to the + * sparse set results in undefined behavior. + * + * @param entt A valid identifier. + * @return The position of the entity in the sparse set. + */ +[[nodiscard]] size_type index(const entity_type entt) const ENTT_NOEXCEPT { +ENTT_ASSERT(contains(entt), "Set does not contain entity"); +return static_cast<size_type>(entity_traits::to_entity(sparse_ref(entt))); +} + +/** + * @brief Returns the entity at specified location, with bounds checking. + * @param pos The position for which to return the entity. + * @return The entity at specified location if any, a null entity otherwise. + */ +[[nodiscard]] entity_type at(const size_type pos) const ENTT_NOEXCEPT { +return pos < packed.size() ? packed[pos] : null; +} + +/** + * @brief Returns the entity at specified location, without bounds checking. + * @param pos The position for which to return the entity. + * @return The entity at specified location. + */ +[[nodiscard]] entity_type operator[](const size_type pos) const ENTT_NOEXCEPT { +ENTT_ASSERT(pos < packed.size(), "Position is out of bounds"); +return packed[pos]; +} + +/** + * @brief Returns the element assigned to an entity, if any. + * + * @warning + * Attempting to use an entity that doesn't belong to the sparse set results + * in undefined behavior. + * + * @param entt A valid identifier. + * @return An opaque pointer to the element assigned to the entity, if any. + */ +const void *get(const entity_type entt) const ENTT_NOEXCEPT { +return get_at(index(entt)); +} + +/*! @copydoc get */ +void *get(const entity_type entt) ENTT_NOEXCEPT { +return const_cast<void *>(std::as_const(*this).get(entt)); +} + +/** + * @brief Assigns an entity to a sparse set. + * + * @warning + * Attempting to assign an entity that already belongs to the sparse set + * results in undefined behavior. + * + * @param entt A valid identifier. + * @param value Optional opaque value 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); +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; +} + +/** + * @brief Assigns one or more entities to a sparse set. + * + * @warning + * Attempting to assign an entity that already belongs to the sparse set + * results in undefined behavior. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return Iterator pointing to the first element inserted in case of + * success, the `end()` iterator otherwise. + */ +template<typename It> +iterator insert(It first, It last) { +for(auto it = first; it != last; ++it) { +try_emplace(*it, true); +} + +return first == last ? end() : find(*first); +} + +/** + * @brief Erases an entity from a sparse set. + * + * @warning + * Attempting to erase an entity that doesn't belong to the sparse set + * results in undefined behavior. + * + * @param entt A valid identifier. + */ +void erase(const entity_type entt) { +const auto it = --(end() - index(entt)); +(mode == deletion_policy::in_place) ? in_place_pop(it, it + 1u) : swap_and_pop(it, it + 1u); +} + +/** + * @brief Erases entities from a set. + * + * @sa erase + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ +template<typename It> +void erase(It first, It last) { +if constexpr(std::is_same_v<It, basic_iterator>) { +(mode == deletion_policy::in_place) ? in_place_pop(first, last) : swap_and_pop(first, last); +} else { +for(; first != last; ++first) { +erase(*first); +} +} +} + +/** + * @brief Removes an entity from a sparse set if it exists. + * @param entt A valid identifier. + * @return True if the entity is actually removed, false otherwise. + */ +bool remove(const entity_type entt) { +return contains(entt) && (erase(entt), true); +} + +/** + * @brief Removes entities from a sparse set if they exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return The number of entities actually removed. + */ +template<typename It> +size_type remove(It first, It last) { +size_type count{}; + +for(; first != last; ++first) { +count += remove(*first); +} + +return count; +} + +/*! @brief Removes all tombstones from the packed array of 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) { +--from; +move_element(from, to); + +using std::swap; +swap(packed[from], 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); +for(; from && packed[from - 1u] == tombstone; --from) {} +} +} + +free_list = tombstone; +packed.resize(from); +} + +/** + * @brief Swaps two entities in a sparse set. + * + * For what it's worth, this function affects both the internal sparse array + * and the internal packed array. Users should not care of that anyway. + * + * @warning + * Attempting to swap entities that don't belong to the sparse set results + * in undefined behavior. + * + * @param lhs A valid identifier. + * @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"); + +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]); +} + +/** + * @brief Sort the first count elements according to the given comparison + * function. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * @endcode + * + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function object must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam 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 length Number of elements to sort. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ +template<typename Compare, typename Sort = std_sort, typename... Args> +void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) { +ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements"); +ENTT_ASSERT(free_list == null, "Partial sorting with tombstones is not supported"); + +algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward<Args>(args)...); + +for(size_type pos{}; pos < length; ++pos) { +auto curr = pos; +auto next = index(packed[curr]); + +while(curr != next) { +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])); +curr = std::exchange(next, idx); +} +} +} + +/** + * @brief Sort all elements according to the given comparison function. + * + * @sa sort_n + * + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ +template<typename Compare, typename Sort = std_sort, typename... Args> +void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { +compact(); +sort_n(packed.size(), std::move(compare), std::move(algo), std::forward<Args>(args)...); +} + +/** + * @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. + * + * @param other The sparse sets that imposes the order of the entities. + */ +void respect(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]) { +// basic no-leak guarantee (with invalid state) if swapping throws +swap_elements(packed[pos], *from); +} + +--pos; +} +} +} + +/*! @brief Clears a sparse set. */ +void clear() { +if(const auto last = end(); free_list == null) { +in_place_pop(begin(), last); +} else { +for(auto &&entity: *this) { +// tombstone filter on itself +if(const auto it = find(entity); it != last) { +in_place_pop(it, it + 1u); +} +} +} + +free_list = tombstone; +packed.clear(); +} + +/** + * @brief Returned value type, if any. + * @return Returned value type, if any. + */ +const type_info &type() const ENTT_NOEXCEPT { +return *info; +} + +/*! @brief Forwards variables to mixins, if any. */ +virtual void bind(any) ENTT_NOEXCEPT {} + +private: +sparse_container_type sparse; +packed_container_type packed; +const type_info *info; +entity_type free_list; +deletion_policy mode; +}; + +} // namespace entt + +#endif + +// #include "entity/storage.hpp" +#ifndef ENTT_ENTITY_STORAGE_HPP +#define ENTT_ENTITY_STORAGE_HPP + +#include <cstddef> +#include <iterator> +#include <memory> +#include <tuple> +#include <type_traits> +#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" + +// #include "component.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sigh_storage_mixin.hpp" + +// #include "sparse_set.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Container> +class storage_iterator final { +friend storage_iterator<const Container>; + +using container_type = std::remove_const_t<Container>; +using alloc_traits = std::allocator_traits<typename container_type::allocator_type>; +using comp_traits = component_traits<typename container_type::value_type>; + +using iterator_traits = std::iterator_traits<std::conditional_t< +std::is_const_v<Container>, +typename alloc_traits::template rebind_traits<typename std::pointer_traits<typename container_type::value_type>::element_type>::const_pointer, +typename alloc_traits::template rebind_traits<typename std::pointer_traits<typename container_type::value_type>::element_type>::pointer>>; + +public: +using value_type = typename iterator_traits::value_type; +using pointer = typename iterator_traits::pointer; +using reference = typename iterator_traits::reference; +using difference_type = typename iterator_traits::difference_type; +using iterator_category = std::random_access_iterator_tag; + +storage_iterator() ENTT_NOEXCEPT = default; + +storage_iterator(Container *ref, difference_type idx) ENTT_NOEXCEPT +: packed{ref}, +offset{idx} {} + +template<bool Const = std::is_const_v<Container>, typename = std::enable_if_t<Const>> +storage_iterator(const storage_iterator<std::remove_const_t<Container>> &other) ENTT_NOEXCEPT +: packed{other.packed}, +offset{other.offset} {} + +storage_iterator &operator++() ENTT_NOEXCEPT { +return --offset, *this; +} + +storage_iterator operator++(int) ENTT_NOEXCEPT { +storage_iterator orig = *this; +return ++(*this), orig; +} + +storage_iterator &operator--() ENTT_NOEXCEPT { +return ++offset, *this; +} + +storage_iterator operator--(int) ENTT_NOEXCEPT { +storage_iterator orig = *this; +return operator--(), orig; +} + +storage_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { +offset -= value; +return *this; +} + +storage_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { +storage_iterator copy = *this; +return (copy += value); +} + +storage_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { +return (*this += -value); +} + +storage_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { +return (*this + -value); +} + +[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { +const auto pos = index() - value; +return (*packed)[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)]; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +const auto pos = index(); +return (*packed)[pos / comp_traits::page_size] + fast_mod(pos, comp_traits::page_size); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return *operator->(); +} + +[[nodiscard]] difference_type index() const ENTT_NOEXCEPT { +return offset - 1; +} + +private: +Container *packed; +difference_type offset; +}; + +template<typename CLhs, typename CRhs> +[[nodiscard]] std::ptrdiff_t operator-(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT { +return rhs.index() - lhs.index(); +} + +template<typename CLhs, typename CRhs> +[[nodiscard]] bool operator==(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT { +return lhs.index() == rhs.index(); +} + +template<typename CLhs, typename CRhs> +[[nodiscard]] bool operator!=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +template<typename CLhs, typename CRhs> +[[nodiscard]] bool operator<(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT { +return lhs.index() > rhs.index(); +} + +template<typename CLhs, typename CRhs> +[[nodiscard]] bool operator>(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT { +return lhs.index() < rhs.index(); +} + +template<typename CLhs, typename CRhs> +[[nodiscard]] bool operator<=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs > rhs); +} + +template<typename CLhs, typename CRhs> +[[nodiscard]] bool operator>=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +template<typename It, typename... Other> +class extended_storage_iterator final { +template<typename Iter, typename... Args> +friend class extended_storage_iterator; + +public: +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; + +extended_storage_iterator() = default; + +extended_storage_iterator(It base, Other... other) +: it{base, other...} {} + +template<typename... Args, typename = std::enable_if_t<(!std::is_same_v<Other, Args> && ...) && (std::is_constructible_v<Other, Args> && ...)>> +extended_storage_iterator(const extended_storage_iterator<It, Args...> &other) +: it{other.it} {} + +extended_storage_iterator &operator++() ENTT_NOEXCEPT { +return ++std::get<It>(it), (++std::get<Other>(it), ...), *this; +} + +extended_storage_iterator operator++(int) ENTT_NOEXCEPT { +extended_storage_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return {*std::get<It>(it), *std::get<Other>(it)...}; +} + +template<typename... CLhs, typename... CRhs> +friend bool operator==(const extended_storage_iterator<CLhs...> &, const extended_storage_iterator<CRhs...> &) ENTT_NOEXCEPT; + +private: +std::tuple<It, Other...> it; +}; + +template<typename... CLhs, typename... CRhs> +[[nodiscard]] bool operator==(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) ENTT_NOEXCEPT { +return std::get<0>(lhs.it) == std::get<0>(rhs.it); +} + +template<typename... CLhs, typename... CRhs> +[[nodiscard]] bool operator!=(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Basic storage implementation. + * + * Internal data structures arrange elements to maximize performance. There are + * no guarantees that objects are returned in the insertion order when iterate + * a storage. Do not make assumption on the order in any case. + * + * @warning + * Empty types aren't explicitly instantiated. Therefore, many of the functions + * normally available for non-empty types will not be available for empty ones. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Type of objects assigned to the entities. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Entity, typename Type, typename Allocator, typename> +class basic_storage: public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> { +static_assert(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>, "The type must be at least move constructible/assignable"); + +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>; + +[[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)]; +} + +auto assure_at_least(const std::size_t pos) { +auto &&container = packed.first(); +const auto idx = pos / comp_traits::page_size; + +if(!(idx < container.size())) { +auto curr = container.size(); +container.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); +} +} +ENTT_CATCH { +container.resize(curr); +ENTT_THROW; +} +} + +return container[idx] + fast_mod(pos, comp_traits::page_size); +} + +template<typename... Args> +auto emplace_element(const Entity entt, const bool force_back, Args &&...args) { +const auto it = base_type::try_emplace(entt, force_back); + +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_CATCH { +if constexpr(comp_traits::in_place_delete) { +base_type::in_place_pop(it, it + 1u); +} else { +base_type::swap_and_pop(it, it + 1u); +} + +ENTT_THROW; +} + +return it; +} + +void shrink_to_size(const std::size_t sz) { +for(auto pos = sz, length = base_type::size(); pos < length; ++pos) { +if constexpr(comp_traits::in_place_delete) { +if(base_type::at(pos) != tombstone) { +std::destroy_at(std::addressof(element_at(pos))); +} +} else { +std::destroy_at(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); +} + +container.resize(from); +} + +private: +const void *get_at(const std::size_t pos) const final { +return std::addressof(element_at(pos)); +} + +void swap_at(const std::size_t lhs, const std::size_t rhs) final { +using std::swap; +swap(element_at(lhs), element_at(rhs)); +} + +void move_element(const std::size_t from, const std::size_t to) final { +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)); +} + +protected: +/** + * @brief Erases elements from a storage. + * @param first An iterator to the first element to erase. + * @param last An iterator past the last element to erase. + */ +void swap_and_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override { +for(; first != last; ++first) { +auto &elem = element_at(base_type::size() - 1u); +// destroying on exit allows reentrant destructors +[[maybe_unused]] auto unused = std::exchange(element_at(static_cast<size_type>(first.index())), std::move(elem)); +std::destroy_at(std::addressof(elem)); +base_type::swap_and_pop(first, first + 1u); +} +} + +/** + * @brief Erases elements from a storage. + * @param first An iterator to the first element to erase. + * @param last An iterator past the last element to erase. + */ +void in_place_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override { +for(; first != last; ++first) { +base_type::in_place_pop(first, first + 1u); +std::destroy_at(std::addressof(element_at(static_cast<size_type>(first.index())))); +} +} + +/** + * @brief Assigns an entity to a storage. + * @param entt A valid identifier. + * @param value Optional opaque value. + * @param force_back Force back insertion. + * @return Iterator pointing to the emplaced element. + */ +typename underlying_type::basic_iterator try_emplace([[maybe_unused]] const Entity entt, 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)); +} else { +return base_type::end(); +} +} else { +if constexpr(std::is_default_constructible_v<value_type>) { +return emplace_element(entt, force_back); +} else { +return base_type::end(); +} +} +} + +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 Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @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>; +/*! @brief Constant random access iterator type. */ +using const_iterator = internal::storage_iterator<const container_type>; +/*! @brief Reverse iterator type. */ +using reverse_iterator = std::reverse_iterator<iterator>; +/*! @brief Constant reverse iterator type. */ +using const_reverse_iterator = std::reverse_iterator<const_iterator>; +/*! @brief Extended iterable storage proxy. */ +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 Default constructor. */ +basic_storage() +: basic_storage{allocator_type{}} {} + +/** + * @brief Constructs an empty storage 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{comp_traits::in_place_delete}, allocator}, +packed{container_type{allocator}, allocator} {} + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +basic_storage(basic_storage &&other) ENTT_NOEXCEPT +: base_type{std::move(other)}, +packed{std::move(other.packed)} {} + +/** + * @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) ENTT_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"); +} + +/*! @brief Default destructor. */ +~basic_storage() override { +shrink_to_size(0u); +} + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ +basic_storage &operator=(basic_storage &&other) ENTT_NOEXCEPT { +ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "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()); +return *this; +} + +/** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ +void swap(basic_storage &other) { +using std::swap; +underlying_type::swap(other); +propagate_on_container_swap(packed.second(), other.packed.second()); +swap(packed.first(), other.packed.first()); +} + +/** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ +[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { +return allocator_type{packed.second()}; +} + +/** + * @brief Increases the capacity of a storage. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ +void reserve(const size_type cap) override { +if(cap != 0u) { +base_type::reserve(cap); +assure_at_least(cap - 1u); +} +} + +/** + * @brief Returns the number of elements that a storage has currently + * allocated space for. + * @return Capacity of the storage. + */ +[[nodiscard]] size_type capacity() const ENTT_NOEXCEPT override { +return packed.first().size() * comp_traits::page_size; +} + +/*! @brief Requests the removal of unused capacity. */ +void shrink_to_fit() override { +base_type::shrink_to_fit(); +shrink_to_size(base_type::size()); +} + +/** + * @brief Direct access to the array of objects. + * @return A pointer to the array of objects. + */ +[[nodiscard]] const_pointer raw() const ENTT_NOEXCEPT { +return packed.first().data(); +} + +/*! @copydoc raw */ +[[nodiscard]] pointer raw() ENTT_NOEXCEPT { +return packed.first().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 ENTT_NOEXCEPT { +const auto pos = static_cast<typename iterator::difference_type>(base_type::size()); +return const_iterator{&packed.first(), pos}; +} + +/*! @copydoc cbegin */ +[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { +return cbegin(); +} + +/*! @copydoc begin */ +[[nodiscard]] iterator begin() ENTT_NOEXCEPT { +const auto pos = static_cast<typename iterator::difference_type>(base_type::size()); +return iterator{&packed.first(), pos}; +} + +/** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the internal array. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the element following the last instance of the + * internal array. + */ +[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { +return const_iterator{&packed.first(), {}}; +} + +/*! @copydoc cend */ +[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { +return cend(); +} + +/*! @copydoc end */ +[[nodiscard]] iterator end() ENTT_NOEXCEPT { +return iterator{&packed.first(), {}}; +} + +/** + * @brief Returns a reverse iterator to the beginning. + * + * The returned iterator points to the first instance of the reversed + * internal array. If the storage is empty, the returned iterator will be + * equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ +[[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT { +return std::make_reverse_iterator(cend()); +} + +/*! @copydoc crbegin */ +[[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT { +return crbegin(); +} + +/*! @copydoc rbegin */ +[[nodiscard]] reverse_iterator rbegin() ENTT_NOEXCEPT { +return std::make_reverse_iterator(end()); +} + +/** + * @brief Returns a reverse iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the reversed internal array. Attempting to dereference the returned + * iterator results in undefined behavior. + * + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ +[[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT { +return std::make_reverse_iterator(cbegin()); +} + +/*! @copydoc crend */ +[[nodiscard]] const_reverse_iterator rend() const ENTT_NOEXCEPT { +return crend(); +} + +/*! @copydoc rend */ +[[nodiscard]] reverse_iterator rend() ENTT_NOEXCEPT { +return std::make_reverse_iterator(begin()); +} + +/** + * @brief Returns the object assigned to an entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return The object assigned to the entity. + */ +[[nodiscard]] const value_type &get(const entity_type entt) const ENTT_NOEXCEPT { +return element_at(base_type::index(entt)); +} + +/*! @copydoc get */ +[[nodiscard]] value_type &get(const entity_type entt) ENTT_NOEXCEPT { +return const_cast<value_type &>(std::as_const(*this).get(entt)); +} + +/** + * @brief Returns the object assigned to an entity as a tuple. + * @param entt A valid identifier. + * @return The object assigned to the entity as a tuple. + */ +[[nodiscard]] std::tuple<const value_type &> get_as_tuple(const entity_type entt) const ENTT_NOEXCEPT { +return std::forward_as_tuple(get(entt)); +} + +/*! @copydoc get_as_tuple */ +[[nodiscard]] std::tuple<value_type &> get_as_tuple(const entity_type entt) ENTT_NOEXCEPT { +return std::forward_as_tuple(get(entt)); +} + +/** + * @brief Assigns an entity to a storage and constructs its object. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + * @param args Parameters to use to construct an object for the entity. + * @return A reference to the newly created object. + */ +template<typename... Args> +value_type &emplace(const entity_type entt, Args &&...args) { +if constexpr(std::is_aggregate_v<value_type>) { +const auto it = emplace_element(entt, false, Type{std::forward<Args>(args)...}); +return element_at(static_cast<size_type>(it.index())); +} else { +const auto it = emplace_element(entt, false, std::forward<Args>(args)...); +return element_at(static_cast<size_type>(it.index())); +} +} + +/** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the updated instance. + */ +template<typename... Func> +value_type &patch(const entity_type entt, Func &&...func) { +const auto idx = base_type::index(entt); +auto &elem = element_at(idx); +(std::forward<Func>(func)(elem), ...); +return elem; +} + +/** + * @brief Assigns one or more entities to a storage and constructs their + * objects from a given instance. + * + * @warning + * Attempting to assign an entity that already belongs to the storage + * results in undefined behavior. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param value An instance of the object to construct. + */ +template<typename It> +void insert(It first, It last, const value_type &value = {}) { +for(; first != last; ++first) { +emplace_element(*first, true, value); +} +} + +/** + * @brief Assigns one or more entities to a storage and constructs their + * objects from a given range. + * + * @sa construct + * + * @tparam EIt Type of input iterator. + * @tparam CIt Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param from An iterator to the first element of the range of objects. + */ +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) { +for(; first != last; ++first, ++from) { +emplace_element(*first, true, *from); +} +} + +/** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity and + * a reference to its component. + * + * @return An iterable object to use to _visit_ the storage. + */ +[[nodiscard]] iterable each() ENTT_NOEXCEPT { +return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}}; +} + +/*! @copydoc each */ +[[nodiscard]] const_iterable each() const ENTT_NOEXCEPT { +return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}}; +} + +private: +compressed_pair<container_type, allocator_type> packed; +}; + +/*! @copydoc basic_storage */ +template<typename Entity, typename Type, typename Allocator> +class basic_storage<Entity, Type, Allocator, std::enable_if_t<ignore_as_empty_v<Type>>> +: 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; +/*! @brief Type of the objects assigned to entities. */ +using value_type = Type; +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief 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 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{comp_traits::in_place_delete}, allocator} {} + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +basic_storage(basic_storage &&other) ENTT_NOEXCEPT = default; + +/** + * @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) ENTT_NOEXCEPT +: base_type{std::move(other), allocator} {} + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ +basic_storage &operator=(basic_storage &&other) ENTT_NOEXCEPT = default; + +/** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ +[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { +return allocator_type{base_type::get_allocator()}; +} + +/** + * @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 ENTT_NOEXCEPT { +ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); +} + +/** + * @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 ENTT_NOEXCEPT { +ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); +return std::tuple{}; +} + +/** + * @brief Assigns an entity to a storage and constructs its object. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + */ +template<typename... Args> +void emplace(const entity_type entt, Args &&...) { +base_type::try_emplace(entt, false); +} + +/** + * @brief Updates the instance assigned to a given entity in-place. + * @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 entities to a storage. + * @tparam It Type of input iterator. + * @tparam Args Types of optional arguments. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ +template<typename It, typename... Args> +void insert(It first, It last, Args &&...) { +for(; first != last; ++first) { +base_type::try_emplace(*first, true); +} +} + +/** + * @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() ENTT_NOEXCEPT { +return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}}; +} + +/*! @copydoc each */ +[[nodiscard]] const_iterable each() const ENTT_NOEXCEPT { +return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}}; +} +}; + +/** + * @brief Provides a common way to access certain properties of storage types. + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Type of objects managed by the storage class. + */ +template<typename Entity, typename Type, typename = void> +struct storage_traits { +/*! @brief Resulting type after component-to-storage conversion. */ +using storage_type = sigh_storage_mixin<basic_storage<Entity, Type>>; +}; + +} // namespace entt + +#endif + +// #include "entity/utility.hpp" +#ifndef ENTT_ENTITY_UTILITY_HPP +#define ENTT_ENTITY_UTILITY_HPP + +// #include "../core/type_traits.hpp" + + +namespace entt { + +/** + * @brief Alias for exclusion lists. + * @tparam Type List of types. + */ +template<typename... Type> +struct exclude_t: type_list<Type...> {}; + +/** + * @brief Variable template for exclusion lists. + * @tparam Type List of types. + */ +template<typename... Type> +inline constexpr exclude_t<Type...> exclude{}; + +/** + * @brief Alias for lists of observed components. + * @tparam Type List of types. + */ +template<typename... Type> +struct get_t: type_list<Type...> {}; + +/** + * @brief Variable template for lists of observed components. + * @tparam Type List of types. + */ +template<typename... Type> +inline constexpr get_t<Type...> get{}; + +/** + * @brief Alias for lists of owned components. + * @tparam Type List of types. + */ +template<typename... Type> +struct owned_t: type_list<Type...> {}; + +/** + * @brief Variable template for lists of owned components. + * @tparam Type List of types. + */ +template<typename... Type> +inline constexpr owned_t<Type...> owned{}; + +} // namespace entt + +#endif + +// #include "entity/view.hpp" +#ifndef ENTT_ENTITY_VIEW_HPP +#define ENTT_ENTITY_VIEW_HPP + +#include <algorithm> +#include <array> +#include <iterator> +#include <tuple> +#include <type_traits> +#include <utility> +// #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" + +// #include "utility.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type, std::size_t Component, std::size_t Exclude> +class view_iterator final { +using iterator_type = typename Type::const_iterator; + +[[nodiscard]] bool valid() const ENTT_NOEXCEPT { +return ((Component != 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); +} + +public: +using value_type = typename iterator_type::value_type; +using pointer = typename iterator_type::pointer; +using reference = typename iterator_type::reference; +using difference_type = typename iterator_type::difference_type; +using iterator_category = std::forward_iterator_tag; + +view_iterator() ENTT_NOEXCEPT = default; + +view_iterator(iterator_type curr, iterator_type to, std::array<const Type *, Component> all_of, std::array<const Type *, Exclude> none_of) ENTT_NOEXCEPT +: it{curr}, +last{to}, +pools{all_of}, +filter{none_of} { +if(it != last && !valid()) { +++(*this); +} +} + +view_iterator &operator++() ENTT_NOEXCEPT { +while(++it != last && !valid()) {} +return *this; +} + +view_iterator operator++(int) ENTT_NOEXCEPT { +view_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return &*it; +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return *operator->(); +} + +template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs> +friend bool operator==(const view_iterator<LhsType, LhsArgs...> &, const view_iterator<RhsType, RhsArgs...> &) ENTT_NOEXCEPT; + +private: +iterator_type it; +iterator_type last; +std::array<const Type *, Component> pools; +std::array<const Type *, Exclude> filter; +}; + +template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs> +[[nodiscard]] bool operator==(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) ENTT_NOEXCEPT { +return lhs.it == rhs.it; +} + +template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs> +[[nodiscard]] bool operator!=(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +template<typename It, typename... Storage> +struct extended_view_iterator final { +using difference_type = std::ptrdiff_t; +using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::declval<Storage>().get_as_tuple({})...)); +using pointer = input_iterator_pointer<value_type>; +using reference = value_type; +using iterator_category = std::input_iterator_tag; + +extended_view_iterator() = default; + +extended_view_iterator(It from, std::tuple<Storage *...> storage) +: it{from}, +pools{storage} {} + +extended_view_iterator &operator++() ENTT_NOEXCEPT { +return ++it, *this; +} + +extended_view_iterator operator++(int) ENTT_NOEXCEPT { +extended_view_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return std::apply([entt = *it](auto *...curr) { return std::tuple_cat(std::make_tuple(entt), curr->get_as_tuple(entt)...); }, pools); +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +template<typename... Lhs, typename... Rhs> +friend bool operator==(const extended_view_iterator<Lhs...> &, const extended_view_iterator<Rhs...> &) ENTT_NOEXCEPT; + +private: +It it; +std::tuple<Storage *...> pools; +}; + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] bool operator==(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) ENTT_NOEXCEPT { +return lhs.it == rhs.it; +} + +template<typename... Lhs, typename... Rhs> +[[nodiscard]] bool operator!=(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief View implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template<typename, typename, typename, typename> +class basic_view; + +/** + * @brief Multi component view. + * + * Multi component views iterate over those entities that have at least all the + * given components in their bags. During initialization, a multi component view + * looks at the number of entities available for each component and uses the + * smallest set in order to get a performance boost when iterate. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the view in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Types of components iterated by the view. + * @tparam Exclude Types of components used to filter the view. + */ +template<typename Entity, typename... Component, typename... Exclude> +class basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>> { +template<typename, typename, typename, typename> +friend class basic_view; + +template<typename Comp> +using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>; + +template<std::size_t... Index> +[[nodiscard]] auto pools_to_array(std::index_sequence<Index...>) const ENTT_NOEXCEPT { +std::size_t pos{}; +std::array<const base_type *, sizeof...(Component) - 1u> other{}; +(static_cast<void>(std::get<Index>(pools) == view ? void() : void(other[pos++] = std::get<Index>(pools))), ...); +return other; +} + +template<std::size_t Comp, std::size_t Other, typename... Args> +[[nodiscard]] auto dispatch_get(const std::tuple<Entity, Args...> &curr) const { +if constexpr(Comp == Other) { +return std::forward_as_tuple(std::get<Args>(curr)...); +} else { +return std::get<Other>(pools)->get_as_tuple(std::get<0>(curr)); +} +} + +template<std::size_t Comp, typename Func, std::size_t... Index> +void each(Func func, std::index_sequence<Index...>) const { +for(const auto curr: std::get<Comp>(pools)->each()) { +const auto entt = std::get<0>(curr); + +if(((sizeof...(Component) != 1u) || (entt != tombstone)) +&& ((Comp == Index || std::get<Index>(pools)->contains(entt)) && ...) +&& std::apply([entt](const auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { +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<Comp, Index>(curr)...)); +} else { +std::apply(func, std::tuple_cat(dispatch_get<Comp, Index>(curr)...)); +} +} +} +} + +template<typename Func, std::size_t... Index> +void pick_and_each(Func func, std::index_sequence<Index...> seq) const { +((std::get<Index>(pools) == view ? each<Index>(std::move(func), seq) : void()), ...); +} + +public: +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Common type among all storage types. */ +using base_type = std::common_type_t<typename storage_type<Component>::base_type...>; +/*! @brief Bidirectional iterator type. */ +using iterator = internal::view_iterator<base_type, sizeof...(Component) - 1u, sizeof...(Exclude)>; +/*! @brief Iterable view type. */ +using iterable = iterable_adaptor<internal::extended_view_iterator<iterator, storage_type<Component>...>>; + +/*! @brief Default constructor to use to create empty, invalid views. */ +basic_view() ENTT_NOEXCEPT +: pools{}, +filter{}, +view{} {} + +/** + * @brief Constructs a multi-type view from a set of storage classes. + * @param component The storage for the types to iterate. + * @param epool The storage for the types used to filter the view. + */ +basic_view(storage_type<Component> &...component, const storage_type<Exclude> &...epool) ENTT_NOEXCEPT +: pools{&component...}, +filter{&epool...}, +view{(std::min)({&static_cast<const base_type &>(component)...}, [](auto *lhs, auto *rhs) { return lhs->size() < rhs->size(); })} {} + +/** + * @brief Creates a new view driven by a given component in its iterations. + * @tparam Comp Type of component used to drive the iteration. + * @return A new view driven by the given component in its iterations. + */ +template<typename Comp> +[[nodiscard]] basic_view use() const ENTT_NOEXCEPT { +basic_view other{*this}; +other.view = std::get<storage_type<Comp> *>(pools); +return other; +} + +/** + * @brief Creates a new view driven by a given component in its iterations. + * @tparam Comp Index of the component used to drive the iteration. + * @return A new view driven by the given component in its iterations. + */ +template<std::size_t Comp> +[[nodiscard]] basic_view use() const ENTT_NOEXCEPT { +basic_view other{*this}; +other.view = std::get<Comp>(pools); +return other; +} + +/** + * @brief Returns the leading storage of a view. + * @return The leading storage of the view. + */ +const base_type &handle() const ENTT_NOEXCEPT { +return *view; +} + +/** + * @brief Returns the storage for a given component type. + * @tparam Comp Type of component of which to return the storage. + * @return The storage for the given component type. + */ +template<typename Comp> +[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { +return *std::get<storage_type<Comp> *>(pools); +} + +/** + * @brief Returns the storage for a given component type. + * @tparam Comp Index of component of which to return the storage. + * @return The storage for the given component type. + */ +template<std::size_t Comp> +[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { +return *std::get<Comp>(pools); +} + +/** + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. + */ +[[nodiscard]] size_type size_hint() const ENTT_NOEXCEPT { +return view->size(); +} + +/** + * @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()`. + * + * @return An iterator to the first entity of the view. + */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return iterator{view->begin(), view->end(), pools_to_array(std::index_sequence_for<Component...>{}), filter}; +} + +/** + * @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 ENTT_NOEXCEPT { +return iterator{view->end(), view->end(), pools_to_array(std::index_sequence_for<Component...>{}), filter}; +} + +/** + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity + * otherwise. + */ +[[nodiscard]] entity_type front() const ENTT_NOEXCEPT { +const auto it = begin(); +return it != end() ? *it : null; +} + +/** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ +[[nodiscard]] entity_type back() const ENTT_NOEXCEPT { +auto it = view->rbegin(); +for(const auto last = view->rend(); it != last && !contains(*it); ++it) {} +return it == view->rend() ? null : *it; +} + +/** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ +[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT { +return contains(entt) ? iterator{view->find(entt), view->end(), pools_to_array(std::index_sequence_for<Component...>{}), filter} : end(); +} + +/** + * @brief Returns the components assigned to the given entity. + * @param entt A valid identifier. + * @return The components assigned to the given entity. + */ +[[nodiscard]] decltype(auto) operator[](const entity_type entt) const { +return get<Component...>(entt); +} + +/** + * @brief Checks if a view is properly initialized. + * @return True if the view is properly initialized, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return view != nullptr; +} + +/** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ +[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { +return std::apply([entt](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) +&& std::apply([entt](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter); +} + +/** + * @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 Comp Types of components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ +template<typename... Comp> +[[nodiscard]] decltype(auto) get(const entity_type entt) const { +ENTT_ASSERT(contains(entt), "View does not contain entity"); + +if constexpr(sizeof...(Comp) == 0) { +return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); +} else if constexpr(sizeof...(Comp) == 1) { +return (std::get<storage_type<Comp> *>(pools)->get(entt), ...); +} else { +return std::tuple_cat(std::get<storage_type<Comp> *>(pools)->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 { +ENTT_ASSERT(contains(entt), "View does not contain entity"); + +if constexpr(sizeof...(Other) == 0) { +return std::get<First>(pools)->get(entt); +} else { +return std::tuple_cat(std::get<First>(pools)->get_as_tuple(entt), std::get<Other>(pools)->get_as_tuple(entt)...); +} +} + +/** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.<br/> + * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ +template<typename Func> +void each(Func func) const { +pick_and_each(std::move(func), std::index_sequence_for<Component...>{}); +} + +/** + * @brief Returns an iterable object to use to _visit_ a view. + * + * The iterable object returns a tuple that contains the current entity and + * a set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @return An iterable object to use to _visit_ the view. + */ +[[nodiscard]] iterable each() const ENTT_NOEXCEPT { +return {internal::extended_view_iterator{begin(), pools}, internal::extended_view_iterator{end(), pools}}; +} + +/** + * @brief Combines two views in a _more specific_ one (friend function). + * @tparam Get Component list of the view to combine with. + * @tparam Excl Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ +template<typename... Get, typename... Excl> +[[nodiscard]] auto operator|(const basic_view<Entity, get_t<Get...>, exclude_t<Excl...>> &other) const ENTT_NOEXCEPT { +using view_type = basic_view<Entity, get_t<Component..., Get...>, exclude_t<Exclude..., Excl...>>; +return std::make_from_tuple<view_type>(std::tuple_cat( +std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, pools), +std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, other.pools), +std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast<const storage_type<Exclude> &>(*curr)...); }, filter), +std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast<const storage_type<Excl> &>(*curr)...); }, other.filter))); +} + +private: +std::tuple<storage_type<Component> *...> pools; +std::array<const base_type *, sizeof...(Exclude)> filter; +const base_type *view; +}; + +/** + * @brief Single component view specialization. + * + * Single component views are specialized in order to get a boost in terms of + * performance. This kind of views can access the underlying data structure + * directly and avoid superfluous checks. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given component are created and assigned to entities. + * * The entity currently pointed is modified (as an example, the given + * component is removed from the entity to which the iterator points). + * * 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. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Type of component iterated by the view. + */ +template<typename Entity, typename Component> +class basic_view<Entity, get_t<Component>, exclude_t<>, std::void_t<std::enable_if_t<!component_traits<std::remove_const_t<Component>>::in_place_delete>>> { +template<typename, typename, typename, typename> +friend class basic_view; + +using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Component>>::storage_type, Component>; + +public: +/*! @brief Underlying entity identifier. */ +using entity_type = Entity; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Common type among all storage types. */ +using base_type = typename storage_type::base_type; +/*! @brief Random access iterator type. */ +using iterator = typename base_type::iterator; +/*! @brief Reversed iterator type. */ +using reverse_iterator = typename base_type::reverse_iterator; +/*! @brief Iterable view type. */ +using iterable = decltype(std::declval<storage_type>().each()); + +/*! @brief Default constructor to use to create empty, invalid views. */ +basic_view() ENTT_NOEXCEPT +: pools{}, +filter{}, +view{} {} + +/** + * @brief Constructs a single-type view from a storage class. + * @param ref The storage for the type to iterate. + */ +basic_view(storage_type &ref) ENTT_NOEXCEPT +: pools{&ref}, +filter{}, +view{&ref} {} + +/** + * @brief Returns the leading storage of a view. + * @return The leading storage of the view. + */ +const base_type &handle() const ENTT_NOEXCEPT { +return *view; +} + +/** + * @brief Returns the storage for a given component type. + * @tparam Comp Type of component of which to return the storage. + * @return The storage for the given component type. + */ +template<typename Comp = Component> +[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { +static_assert(std::is_same_v<Comp, Component>, "Invalid component type"); +return *std::get<0>(pools); +} + +/** + * @brief Returns the storage for a given component type. + * @tparam Comp Index of component of which to return the storage. + * @return The storage for the given component type. + */ +template<std::size_t Comp> +[[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { +return *std::get<Comp>(pools); +} + +/** + * @brief Returns the number of entities that have the given component. + * @return Number of entities that have the given component. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return view->size(); +} + +/** + * @brief Checks whether a view is empty. + * @return True if the view is empty, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return 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()`. + * + * @return An iterator to the first entity of the view. + */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return view->begin(); +} + +/** + * @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 ENTT_NOEXCEPT { +return view->end(); +} + +/** + * @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()`. + * + * @return An iterator to the first entity of the reversed view. + */ +[[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { +return view->rbegin(); +} + +/** + * @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 ENTT_NOEXCEPT { +return view->rend(); +} + +/** + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity + * otherwise. + */ +[[nodiscard]] entity_type front() const ENTT_NOEXCEPT { +return empty() ? null : *begin(); +} + +/** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ +[[nodiscard]] entity_type back() const ENTT_NOEXCEPT { +return empty() ? null : *rbegin(); +} + +/** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ +[[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT { +return contains(entt) ? view->find(entt) : end(); +} + +/** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ +[[nodiscard]] entity_type operator[](const size_type pos) const { +return begin()[pos]; +} + +/** + * @brief Returns the component assigned to the given entity. + * @param entt A valid identifier. + * @return The component assigned to the given entity. + */ +[[nodiscard]] decltype(auto) operator[](const entity_type entt) const { +return get<Component>(entt); +} + +/** + * @brief Checks if a view is properly initialized. + * @return True if the view is properly initialized, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return view != nullptr; +} + +/** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ +[[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { +return view->contains(entt); +} + +/** + * @brief Returns the component assigned to the given entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the view results in + * undefined behavior. + * + * @tparam Comp Type or index of the component to get. + * @param entt A valid identifier. + * @return The component assigned to the entity. + */ +template<typename... Comp> +[[nodiscard]] decltype(auto) get(const entity_type entt) const { +ENTT_ASSERT(contains(entt), "View does not contain entity"); + +if constexpr(sizeof...(Comp) == 0) { +return std::get<0>(pools)->get_as_tuple(entt); +} else { +static_assert(std::is_same_v<Comp..., Component>, "Invalid component type"); +return std::get<0>(pools)->get(entt); +} +} + +/*! @copydoc get */ +template<std::size_t Comp> +[[nodiscard]] decltype(auto) get(const entity_type entt) const { +ENTT_ASSERT(contains(entt), "View does not contain entity"); +return std::get<0>(pools)->get(entt); +} + +/** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a reference to the component if it's a non-empty one. + * The _constness_ of the component is as requested.<br/> + * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Component &); + * void(Component &); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ +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(std::is_invocable_v<Func, Component &>) { +for(auto &&component: *std::get<0>(pools)) { +func(component); +} +} else if constexpr(std::is_invocable_v<Func, Entity>) { +for(auto entity: *view) { +func(entity); +} +} else { +for(size_type pos{}, last = size(); pos < last; ++pos) { +func(); +} +} +} + +/** + * @brief Returns an iterable object to use to _visit_ a view. + * + * The iterable object returns a tuple that contains the current entity and + * a reference to its component if it's a non-empty one. The _constness_ of + * the component is as requested. + * + * @return An iterable object to use to _visit_ the view. + */ +[[nodiscard]] iterable each() const ENTT_NOEXCEPT { +return std::get<0>(pools)->each(); +} + +/** + * @brief Combines two views in a _more specific_ one (friend function). + * @tparam Get Component list of the view to combine with. + * @tparam Excl Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ +template<typename... Get, typename... Excl> +[[nodiscard]] auto operator|(const basic_view<Entity, get_t<Get...>, exclude_t<Excl...>> &other) const ENTT_NOEXCEPT { +using view_type = basic_view<Entity, get_t<Component, Get...>, exclude_t<Excl...>>; +return std::make_from_tuple<view_type>(std::tuple_cat( +std::forward_as_tuple(*std::get<0>(pools)), +std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, other.pools), +std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast<const typename view_type::template storage_type<Excl> &>(*curr)...); }, other.filter))); +} + +private: +std::tuple<storage_type *> pools; +std::array<const base_type *, 0u> filter; +const base_type *view; +}; + +/** + * @brief Deduction guide. + * @tparam Storage Type of storage classes used to create the view. + * @param storage The storage for the types to iterate. + */ +template<typename... Storage> +basic_view(Storage &...storage) -> basic_view<std::common_type_t<typename Storage::entity_type...>, get_t<constness_as_t<typename Storage::value_type, Storage>...>, exclude_t<>>; + +} // namespace entt + +#endif + +// #include "locator/locator.hpp" +#ifndef ENTT_LOCATOR_LOCATOR_HPP +#define ENTT_LOCATOR_LOCATOR_HPP + +#include <memory> +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +/** + * @brief Service locator, nothing more. + * + * A service locator is used to do what it promises: locate services.<br/> + * Usually service locators are tightly bound to the services they expose and + * thus it's hard to define a general purpose class to do that. This tiny class + * tries to fill the gap and to get rid of the burden of defining a different + * specific locator for each application. + * + * @note + * Users shouldn't retain references to a service. The recommended way is to + * retrieve the service implementation currently set each and every time the + * need for it arises. The risk is to incur in unexpected behaviors otherwise. + * + * @tparam Service Service type. + */ +template<typename Service> +struct locator final { +/*! @brief Service type. */ +using type = Service; + +/*! @brief Default constructor, deleted on purpose. */ +locator() = delete; +/*! @brief Default destructor, deleted on purpose. */ +~locator() = delete; + +/** + * @brief Checks whether a service locator contains a value. + * @return True if the service locator contains a value, false otherwise. + */ +[[nodiscard]] static bool has_value() ENTT_NOEXCEPT { +return (service != nullptr); +} + +/** + * @brief Returns a reference to a valid service, if any. + * + * @warning + * Invoking this function can result in undefined behavior if the service + * hasn't been set yet. + * + * @return A reference to the service currently set, if any. + */ +[[nodiscard]] static Service &value() ENTT_NOEXCEPT { +ENTT_ASSERT(has_value(), "Service not available"); +return *service; +} + +/** + * @brief Returns a service if available or sets it from a fallback type. + * + * Arguments are used only if a service doesn't already exist. In all other + * cases, they are discarded. + * + * @tparam Args Types of arguments to use to construct the fallback service. + * @tparam Impl 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> +[[nodiscard]] static Service &value_or(Args &&...args) { +return service ? *service : emplace<Impl>(std::forward<Args>(args)...); +} + +/** + * @brief Sets or replaces a service. + * @tparam Impl 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> +static Service &emplace(Args &&...args) { +service = std::make_shared<Impl>(std::forward<Args>(args)...); +return *service; +} + +/** + * @brief Sets or replaces a service using a given allocator. + * @tparam Impl 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)...); +return *service; +} + +/*! @brief Resets a service. */ +static void reset() ENTT_NOEXCEPT { +service.reset(); +} + +private: +// std::shared_ptr because of its type erased allocator which is pretty useful here +inline static std::shared_ptr<Service> service = nullptr; +}; + +} // namespace entt + +#endif + +// #include "meta/adl_pointer.hpp" +#ifndef ENTT_META_ADL_POINTER_HPP +#define ENTT_META_ADL_POINTER_HPP + +namespace entt { + +/** + * @brief ADL based lookup function for dereferencing meta pointer-like types. + * @tparam Type Element type. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ +template<typename Type> +decltype(auto) dereference_meta_pointer_like(const Type &value) { +return *value; +} + +/** + * @brief Fake ADL based lookup function for meta pointer-like types. + * @tparam Type Element type. + */ +template<typename Type> +struct adl_meta_pointer_like { +/** + * @brief Uses the default ADL based lookup method to resolve the call. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ +static decltype(auto) dereference(const Type &value) { +return dereference_meta_pointer_like(value); +} +}; + +} // namespace entt + +#endif + +// #include "meta/container.hpp" +#ifndef ENTT_META_CONTAINER_HPP +#define ENTT_META_CONTAINER_HPP + +#include <array> +#include <map> +#include <set> +#include <type_traits> +#include <unordered_map> +#include <unordered_set> +#include <vector> +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include <algorithm> +#include <cmath> +#include <cstddef> +#include <functional> +#include <iterator> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include <cstddef> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include <cstdint> +#include <type_traits> +// #include "../config/config.h" + + +namespace entt { + +template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)> +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type, std::size_t, typename = void> +struct compressed_pair_element { +using reference = Type &; +using const_reference = const Type &; + +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>> +compressed_pair_element() +: value{} {} + +template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>> +compressed_pair_element(Args &&args) +: value{std::forward<Args>(args)} {} + +template<typename... Args, std::size_t... Index> +compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) +: value{std::forward<Args>(std::get<Index>(args))...} {} + +[[nodiscard]] reference get() ENTT_NOEXCEPT { +return value; +} + +[[nodiscard]] const_reference get() const ENTT_NOEXCEPT { +return value; +} + +private: +Type value; +}; + +template<typename Type, std::size_t Tag> +struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type { +using reference = Type &; +using const_reference = const Type &; +using base_type = Type; + +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>> +compressed_pair_element() +: base_type{} {} + +template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>> +compressed_pair_element(Args &&args) +: base_type{std::forward<Args>(args)} {} + +template<typename... Args, std::size_t... Index> +compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) +: base_type{std::forward<Args>(std::get<Index>(args))...} {} + +[[nodiscard]] reference get() ENTT_NOEXCEPT { +return *this; +} + +[[nodiscard]] const_reference get() const ENTT_NOEXCEPT { +return *this; +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +class compressed_pair final +: internal::compressed_pair_element<First, 0u>, +internal::compressed_pair_element<Second, 1u> { +using first_base = internal::compressed_pair_element<First, 0u>; +using second_base = internal::compressed_pair_element<Second, 1u>; + +public: +/*! @brief The type of the first element that the pair stores. */ +using first_type = First; +/*! @brief The type of the second element that the pair stores. */ +using second_type = Second; + +/** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>> +constexpr compressed_pair() +: first_base{}, +second_base{} {} + +/** + * @brief Copy constructor. + * @param other The instance to copy from. + */ +constexpr compressed_pair(const compressed_pair &other) = default; + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +constexpr compressed_pair(compressed_pair &&other) = default; + +/** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ +template<typename Arg, typename Other> +constexpr compressed_pair(Arg &&arg, Other &&other) +: first_base{std::forward<Arg>(arg)}, +second_base{std::forward<Other>(other)} {} + +/** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ +template<typename... Args, typename... Other> +constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) +: first_base{std::move(args), std::index_sequence_for<Args...>{}}, +second_base{std::move(other), std::index_sequence_for<Other...>{}} {} + +/** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ +constexpr compressed_pair &operator=(const compressed_pair &other) = default; + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ +constexpr compressed_pair &operator=(compressed_pair &&other) = default; + +/** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ +[[nodiscard]] first_type &first() ENTT_NOEXCEPT { +return static_cast<first_base &>(*this).get(); +} + +/*! @copydoc first */ +[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { +return static_cast<const first_base &>(*this).get(); +} + +/** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ +[[nodiscard]] second_type &second() ENTT_NOEXCEPT { +return static_cast<second_base &>(*this).get(); +} + +/*! @copydoc second */ +[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { +return static_cast<const second_base &>(*this).get(); +} + +/** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ +void swap(compressed_pair &other) { +using std::swap; +swap(first(), other.first()); +swap(second(), other.second()); +} + +/** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ +template<std::size_t Index> +decltype(auto) get() ENTT_NOEXCEPT { +if constexpr(Index == 0u) { +return first(); +} else { +static_assert(Index == 1u, "Index out of bounds"); +return second(); +} +} + +/*! @copydoc get */ +template<std::size_t Index> +decltype(auto) get() const ENTT_NOEXCEPT { +if constexpr(Index == 0u) { +return first(); +} else { +static_assert(Index == 1u, "Index out of bounds"); +return second(); +} +} +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template<typename Type, typename Other> +compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template<typename First, typename Second> +inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) { +lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<size_t Index, typename First, typename Second> +struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> { +static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include <iterator> +#include <memory> +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template<typename Type> +struct input_iterator_pointer final { +/*! @brief Pointer type. */ +using pointer = Type *; + +/*! @brief Default copy constructor, deleted on purpose. */ +input_iterator_pointer(const input_iterator_pointer &) = delete; + +/*! @brief Default move constructor. */ +input_iterator_pointer(input_iterator_pointer &&) = default; + +/** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ +input_iterator_pointer(Type &&val) +: value{std::move(val)} {} + +/** + * @brief Default copy assignment operator, deleted on purpose. + * @return This proxy object. + */ +input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; + +/** + * @brief Default move assignment operator. + * @return This proxy object. + */ +input_iterator_pointer &operator=(input_iterator_pointer &&) = default; + +/** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ +[[nodiscard]] pointer operator->() ENTT_NOEXCEPT { +return std::addressof(value); +} + +private: +Type value; +}; + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template<typename It, typename Sentinel = It> +struct iterable_adaptor final { +/*! @brief Value type. */ +using value_type = typename std::iterator_traits<It>::value_type; +/*! @brief Iterator type. */ +using iterator = It; +/*! @brief Sentinel type. */ +using sentinel = Sentinel; + +/*! @brief Default constructor. */ +iterable_adaptor() = default; + +/** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ +iterable_adaptor(iterator from, sentinel to) +: first{from}, +last{to} {} + +/** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return first; +} + +/** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ +[[nodiscard]] sentinel end() const ENTT_NOEXCEPT { +return last; +} + +/*! @copydoc begin */ +[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { +return begin(); +} + +/*! @copydoc end */ +[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT { +return end(); +} + +private: +It first; +Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include <cstddef> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template<typename Type> +[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT { +if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) { +return ptr; +} else { +return to_address(std::forward<Type>(ptr).operator->()); +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) { +lhs = rhs; +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) { +lhs = std::move(rhs); +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); + +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) { +using std::swap; +swap(lhs, rhs); +} +} + +/** + * @brief Checks whether a value is a power of two or not. + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value. + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded"); +std::size_t curr = value - (value != 0u); + +for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) { +curr |= curr >> next; +} + +return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT { +ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); +return value & (mod - 1u); +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Args Types of arguments to use to construct the object. + */ +template<typename Allocator> +struct allocation_deleter: private Allocator { +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Pointer type. */ +using pointer = typename std::allocator_traits<Allocator>::pointer; + +/** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ +allocation_deleter(const allocator_type &alloc) +: Allocator{alloc} {} + +/** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ +void operator()(pointer ptr) { +using alloc_traits = typename std::allocator_traits<Allocator>; +alloc_traits::destroy(*this, to_address(ptr)); +alloc_traits::deallocate(*this, ptr, 1u); +} +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template<typename Type, typename Allocator, typename... Args> +auto allocate_unique(Allocator &allocator, Args &&...args) { +static_assert(!std::is_array_v<Type>, "Array types are not supported"); + +using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>; +using allocator_type = typename alloc_traits::allocator_type; + +allocator_type alloc{allocator}; +auto ptr = alloc_traits::allocate(alloc, 1u); + +ENTT_TRY { +alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...); +} +ENTT_CATCH { +alloc_traits::deallocate(alloc, ptr, 1u); +ENTT_THROW; +} + +return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc}; +} + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type> +struct uses_allocator_construction { +template<typename Allocator, typename... Params> +static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT { +if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) { +return std::forward_as_tuple(std::forward<Params>(params)...); +} else { +static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request"); + +if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) { +return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...); +} else { +static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request"); +return std::forward_as_tuple(std::forward<Params>(params)..., allocator); +} +} +} +}; + +template<typename Type, typename Other> +struct uses_allocator_construction<std::pair<Type, Other>> { +using type = std::pair<Type, Other>; + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT { +return std::make_tuple( +std::piecewise_construct, +std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)), +std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second))); +} + +template<typename Allocator> +static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second))); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT { +return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { +return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { +return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include <functional> +#include <memory> + +namespace entt { + +template< +typename Key, +typename Type, +typename = std::hash<Key>, +typename = std::equal_to<Key>, +typename = std::allocator<std::pair<const Key, Type>>> +class dense_map; + +template< +typename Type, +typename = std::hash<Type>, +typename = std::equal_to<Type>, +typename = std::allocator<Type>> +class dense_set; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Key, typename Type> +struct dense_map_node final { +using value_type = std::pair<Key, Type>; + +template<typename... Args> +dense_map_node(const std::size_t pos, Args &&...args) +: next{pos}, +element{std::forward<Args>(args)...} {} + +template<typename Allocator, typename... Args> +dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) +: next{pos}, +element{entt::make_obj_using_allocator<value_type>(allocator, std::forward<Args>(args)...)} {} + +template<typename Allocator> +dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) +: next{other.next}, +element{entt::make_obj_using_allocator<value_type>(allocator, other.element)} {} + +template<typename Allocator> +dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) +: next{other.next}, +element{entt::make_obj_using_allocator<value_type>(allocator, std::move(other.element))} {} + +std::size_t next; +value_type element; +}; + +template<typename It> +class dense_map_iterator final { +template<typename> +friend class dense_map_iterator; + +using first_type = decltype(std::as_const(std::declval<It>()->element.first)); +using second_type = decltype((std::declval<It>()->element.second)); + +public: +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; + +dense_map_iterator() ENTT_NOEXCEPT +: it{} {} + +dense_map_iterator(const It iter) ENTT_NOEXCEPT +: it{iter} {} + +template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> +dense_map_iterator(const dense_map_iterator<Other> &other) ENTT_NOEXCEPT +: it{other.it} {} + +dense_map_iterator &operator++() ENTT_NOEXCEPT { +return ++it, *this; +} + +dense_map_iterator operator++(int) ENTT_NOEXCEPT { +dense_map_iterator orig = *this; +return ++(*this), orig; +} + +dense_map_iterator &operator--() ENTT_NOEXCEPT { +return --it, *this; +} + +dense_map_iterator operator--(int) ENTT_NOEXCEPT { +dense_map_iterator orig = *this; +return operator--(), orig; +} + +dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { +it += value; +return *this; +} + +dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { +dense_map_iterator copy = *this; +return (copy += value); +} + +dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { +return (*this += -value); +} + +dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { +return (*this + -value); +} + +[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { +return {it[value].element.first, it[value].element.second}; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return {it->element.first, it->element.second}; +} + +template<typename ILhs, typename IRhs> +friend std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT; + +template<typename ILhs, typename IRhs> +friend bool operator==(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT; + +template<typename ILhs, typename IRhs> +friend bool operator<(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT; + +private: +It it; +}; + +template<typename ILhs, typename IRhs> +[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it - rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator==(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it == rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator!=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator<(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it < rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator>(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator<=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs > rhs); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator>=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +template<typename It> +class dense_map_local_iterator final { +template<typename> +friend class dense_map_local_iterator; + +using first_type = decltype(std::as_const(std::declval<It>()->element.first)); +using second_type = decltype((std::declval<It>()->element.second)); + +public: +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; + +dense_map_local_iterator() ENTT_NOEXCEPT +: it{}, +offset{} {} + +dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT +: it{iter}, +offset{pos} {} + +template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> +dense_map_local_iterator(const dense_map_local_iterator<Other> &other) ENTT_NOEXCEPT +: it{other.it}, +offset{other.offset} {} + +dense_map_local_iterator &operator++() ENTT_NOEXCEPT { +return offset = it[offset].next, *this; +} + +dense_map_local_iterator operator++(int) ENTT_NOEXCEPT { +dense_map_local_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return {it[offset].element.first, it[offset].element.second}; +} + +[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { +return offset; +} + +private: +It it; +std::size_t offset; +}; + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator==(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.index() == rhs.index(); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator!=(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Key, typename Type, typename Hash, typename KeyEqual, typename Allocator> +class dense_map { +static constexpr float default_threshold = 0.875f; +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>; +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>>; + +template<typename Other> +[[nodiscard]] std::size_t key_to_bucket(const Other &key) const ENTT_NOEXCEPT { +return fast_mod(sparse.second()(key), bucket_count()); +} + +template<typename Other> +[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { +for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { +if(packed.second()(it->first, key)) { +return begin() + static_cast<typename iterator::difference_type>(it.index()); +} +} + +return end(); +} + +template<typename Other> +[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { +for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { +if(packed.second()(it->first, key)) { +return cbegin() + static_cast<typename iterator::difference_type>(it.index()); +} +} + +return cend(); +} + +template<typename Other, typename... Args> +[[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { +const auto index = key_to_bucket(key); + +if(auto it = constrained_find(key, index); it != end()) { +return std::make_pair(it, false); +} + +packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward<Other>(key)), std::forward_as_tuple(std::forward<Args>(args)...)); +sparse.first()[index] = packed.first().size() - 1u; +rehash_if_required(); + +return std::make_pair(--end(), true); +} + +template<typename Other, typename Arg> +[[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { +const auto index = key_to_bucket(key); + +if(auto it = constrained_find(key, index); it != end()) { +it->second = std::forward<Arg>(value); +return std::make_pair(it, false); +} + +packed.first().emplace_back(sparse.first()[index], std::forward<Other>(key), std::forward<Arg>(value)); +sparse.first()[index] = packed.first().size() - 1u; +rehash_if_required(); + +return std::make_pair(--end(), true); +} + +void move_and_pop(const std::size_t pos) { +if(const auto last = size() - 1u; pos != last) { +packed.first()[pos] = std::move(packed.first().back()); +size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); +for(; *curr != last; curr = &packed.first()[*curr].next) {} +*curr = pos; +} + +packed.first().pop_back(); +} + +void rehash_if_required() { +if(size() > (bucket_count() * max_load_factor())) { +rehash(bucket_count() * 2u); +} +} + +public: +/*! @brief Key type of the container. */ +using key_type = Key; +/*! @brief Mapped type of the container. */ +using mapped_type = Type; +/*! @brief Key-value type of the container. */ +using value_type = std::pair<const Key, Type>; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Type of function to use to hash the keys. */ +using hasher = Hash; +/*! @brief Type of function to use to compare the keys for equality. */ +using key_equal = KeyEqual; +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Input iterator type. */ +using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>; +/*! @brief Constant input iterator type. */ +using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_iterator>; +/*! @brief Input iterator type. */ +using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::iterator>; +/*! @brief Constant input iterator type. */ +using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_iterator>; + +/*! @brief Default constructor. */ +dense_map() +: dense_map(minimum_capacity) {} + +/** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ +explicit dense_map(const allocator_type &allocator) +: dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param allocator The allocator to use. + */ +dense_map(const size_type bucket_count, const allocator_type &allocator) +: dense_map{bucket_count, hasher{}, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ +dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) +: dense_map{bucket_count, hash, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ +explicit dense_map(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) +: sparse{allocator, hash}, +packed{allocator, equal}, +threshold{default_threshold} { +rehash(bucket_count); +} + +/*! @brief Default copy constructor. */ +dense_map(const dense_map &) = default; + +/** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ +dense_map(const dense_map &other, const allocator_type &allocator) +: sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, +packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, +threshold{other.threshold} {} + +/*! @brief Default move constructor. */ +dense_map(dense_map &&) = default; + +/** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ +dense_map(dense_map &&other, const allocator_type &allocator) +: sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, +packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, +threshold{other.threshold} {} + +/** + * @brief Default copy assignment operator. + * @return This container. + */ +dense_map &operator=(const dense_map &) = default; + +/** + * @brief Default move assignment operator. + * @return This container. + */ +dense_map &operator=(dense_map &&) = default; + +/** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ +[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { +return sparse.first().get_allocator(); +} + +/** + * @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. + */ +[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { +return packed.first().begin(); +} + +/*! @copydoc cbegin */ +[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { +return cbegin(); +} + +/*! @copydoc begin */ +[[nodiscard]] iterator begin() ENTT_NOEXCEPT { +return packed.first().begin(); +} + +/** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the internal array. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the element following the last instance of the + * internal array. + */ +[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { +return packed.first().end(); +} + +/*! @copydoc cend */ +[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { +return cend(); +} + +/*! @copydoc end */ +[[nodiscard]] iterator end() ENTT_NOEXCEPT { +return packed.first().end(); +} + +/** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return packed.first().empty(); +} + +/** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return packed.first().size(); +} + +/*! @brief Clears the container. */ +void clear() ENTT_NOEXCEPT { +sparse.first().clear(); +packed.first().clear(); +rehash(0u); +} + +/** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +std::pair<iterator, bool> insert(const value_type &value) { +return insert_or_do_nothing(value.first, value.second); +} + +/*! @copydoc insert */ +std::pair<iterator, bool> insert(value_type &&value) { +return insert_or_do_nothing(std::move(value.first), std::move(value.second)); +} + +/** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ +template<typename Arg> +std::enable_if_t<std::is_constructible_v<value_type, Arg &&>, std::pair<iterator, bool>> +insert(Arg &&value) { +return insert_or_do_nothing(std::forward<Arg>(value).first, std::forward<Arg>(value).second); +} + +/** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ +template<typename It> +void insert(It first, It last) { +for(; first != last; ++first) { +insert(*first); +} +} + +/** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ +template<typename Arg> +std::pair<iterator, bool> insert_or_assign(const key_type &key, Arg &&value) { +return insert_or_overwrite(key, std::forward<Arg>(value)); +} + +/*! @copydoc insert_or_assign */ +template<typename Arg> +std::pair<iterator, bool> insert_or_assign(key_type &&key, Arg &&value) { +return insert_or_overwrite(std::move(key), std::forward<Arg>(value)); +} + +/** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +template<typename... Args> +std::pair<iterator, bool> emplace([[maybe_unused]] Args &&...args) { +if constexpr(sizeof...(Args) == 0u) { +return insert_or_do_nothing(key_type{}); +} else if constexpr(sizeof...(Args) == 1u) { +return insert_or_do_nothing(std::forward<Args>(args).first..., std::forward<Args>(args).second...); +} else if constexpr(sizeof...(Args) == 2u) { +return insert_or_do_nothing(std::forward<Args>(args)...); +} else { +auto &node = packed.first().emplace_back(packed.first().size(), std::forward<Args>(args)...); +const auto index = key_to_bucket(node.element.first); + +if(auto it = constrained_find(node.element.first, index); it != end()) { +packed.first().pop_back(); +return std::make_pair(it, false); +} + +std::swap(node.next, sparse.first()[index]); +rehash_if_required(); + +return std::make_pair(--end(), true); +} +} + +/** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +template<typename... Args> +std::pair<iterator, bool> try_emplace(const key_type &key, Args &&...args) { +return insert_or_do_nothing(key, std::forward<Args>(args)...); +} + +/*! @copydoc try_emplace */ +template<typename... Args> +std::pair<iterator, bool> try_emplace(key_type &&key, Args &&...args) { +return insert_or_do_nothing(std::move(key), std::forward<Args>(args)...); +} + +/** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ +iterator erase(const_iterator pos) { +const auto diff = pos - cbegin(); +erase(pos->first); +return begin() + diff; +} + +/** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ +iterator erase(const_iterator first, const_iterator last) { +const auto dist = first - cbegin(); + +for(auto from = last - cbegin(); from != dist; --from) { +erase(packed.first()[from - 1u].element.first); +} + +return (begin() + dist); +} + +/** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ +size_type erase(const key_type &key) { +for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) { +if(packed.second()(packed.first()[*curr].element.first, key)) { +const auto index = *curr; +*curr = packed.first()[*curr].next; +move_and_pop(index); +return 1u; +} +} + +return 0u; +} + +/** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ +void swap(dense_map &other) { +using std::swap; +swap(sparse, other.sparse); +swap(packed, other.packed); +swap(threshold, other.threshold); +} + +/** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ +[[nodiscard]] mapped_type &at(const key_type &key) { +auto it = find(key); +ENTT_ASSERT(it != end(), "Invalid key"); +return it->second; +} + +/*! @copydoc at */ +[[nodiscard]] const mapped_type &at(const key_type &key) const { +auto it = find(key); +ENTT_ASSERT(it != cend(), "Invalid key"); +return it->second; +} + +/** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ +[[nodiscard]] mapped_type &operator[](const key_type &key) { +return insert_or_do_nothing(key).first->second; +} + +/** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ +[[nodiscard]] mapped_type &operator[](key_type &&key) { +return insert_or_do_nothing(std::move(key)).first->second; +} + +/** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ +[[nodiscard]] iterator find(const key_type &key) { +return constrained_find(key, key_to_bucket(key)); +} + +/*! @copydoc find */ +[[nodiscard]] const_iterator find(const key_type &key) const { +return constrained_find(key, key_to_bucket(key)); +} + +/** + * @brief Finds an element with a key that compares _equivalent_ to a given + * value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>> +find(const Other &key) { +return constrained_find(key, key_to_bucket(key)); +} + +/*! @copydoc find */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>> +find(const Other &key) const { +return constrained_find(key, key_to_bucket(key)); +} + +/** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ +[[nodiscard]] bool contains(const key_type &key) const { +return (find(key) != cend()); +} + +/** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>> +contains(const Other &key) const { +return (find(key) != cend()); +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] const_local_iterator cbegin(const size_type index) const { +return {packed.first().begin(), sparse.first()[index]}; +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] const_local_iterator begin(const size_type index) const { +return cbegin(index); +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] local_iterator begin(const size_type index) { +return {packed.first().begin(), sparse.first()[index]}; +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { +return {packed.first().begin(), (std::numeric_limits<size_type>::max)()}; +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const { +return cend(index); +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { +return {packed.first().begin(), (std::numeric_limits<size_type>::max)()}; +} + +/** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ +[[nodiscard]] size_type bucket_count() const { +return sparse.first().size(); +} + +/** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ +[[nodiscard]] size_type max_bucket_count() const { +return sparse.first().max_size(); +} + +/** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ +[[nodiscard]] size_type bucket_size(const size_type index) const { +return static_cast<size_type>(std::distance(begin(index), end(index))); +} + +/** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ +[[nodiscard]] size_type bucket(const key_type &key) const { +return key_to_bucket(key); +} + +/** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ +[[nodiscard]] float load_factor() const { +return size() / static_cast<float>(bucket_count()); +} + +/** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ +[[nodiscard]] float max_load_factor() const { +return threshold; +} + +/** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ +void max_load_factor(const float value) { +ENTT_ASSERT(value > 0.f, "Invalid load factor"); +threshold = value; +rehash(0u); +} + +/** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param count New number of buckets. + */ +void rehash(const size_type count) { +auto value = (std::max)(count, minimum_capacity); +value = (std::max)(value, static_cast<size_type>(size() / max_load_factor())); + +if(const auto sz = next_power_of_two(value); sz != bucket_count()) { +sparse.first().resize(sz); +std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits<size_type>::max)()); + +for(size_type pos{}, last = size(); pos < last; ++pos) { +const auto index = key_to_bucket(packed.first()[pos].element.first); +packed.first()[pos].next = std::exchange(sparse.first()[index], pos); +} +} +} + +/** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param count New number of elements. + */ +void reserve(const size_type count) { +packed.first().reserve(count); +rehash(static_cast<size_type>(std::ceil(count / max_load_factor()))); +} + +/** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ +[[nodiscard]] hasher hash_function() const { +return sparse.second(); +} + +/** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ +[[nodiscard]] key_equal key_eq() const { +return packed.second(); +} + +private: +compressed_pair<sparse_container_type, hasher> sparse; +compressed_pair<packed_container_type, key_equal> packed; +float threshold; +}; + +} // namespace entt + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace std { + +template<typename Key, typename Value, typename Allocator> +struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator> +: std::true_type {}; + +} // namespace std + +/** + * Internal details not to be documented. + * @endcond + */ + +#endif + +// #include "../container/dense_set.hpp" +#ifndef ENTT_CONTAINER_DENSE_SET_HPP +#define ENTT_CONTAINER_DENSE_SET_HPP + +#include <algorithm> +#include <cmath> +#include <cstddef> +#include <functional> +#include <iterator> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename It> +class dense_set_iterator final { +template<typename> +friend class dense_set_iterator; + +public: +using value_type = typename It::value_type::second_type; +using pointer = const value_type *; +using reference = const value_type &; +using difference_type = std::ptrdiff_t; +using iterator_category = std::random_access_iterator_tag; + +dense_set_iterator() ENTT_NOEXCEPT +: it{} {} + +dense_set_iterator(const It iter) ENTT_NOEXCEPT +: it{iter} {} + +template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> +dense_set_iterator(const dense_set_iterator<Other> &other) ENTT_NOEXCEPT +: it{other.it} {} + +dense_set_iterator &operator++() ENTT_NOEXCEPT { +return ++it, *this; +} + +dense_set_iterator operator++(int) ENTT_NOEXCEPT { +dense_set_iterator orig = *this; +return ++(*this), orig; +} + +dense_set_iterator &operator--() ENTT_NOEXCEPT { +return --it, *this; +} + +dense_set_iterator operator--(int) ENTT_NOEXCEPT { +dense_set_iterator orig = *this; +return operator--(), orig; +} + +dense_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { +it += value; +return *this; +} + +dense_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { +dense_set_iterator copy = *this; +return (copy += value); +} + +dense_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { +return (*this += -value); +} + +dense_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { +return (*this + -value); +} + +[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { +return it[value].second; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return std::addressof(it->second); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return *operator->(); +} + +template<typename ILhs, typename IRhs> +friend std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT; + +template<typename ILhs, typename IRhs> +friend bool operator==(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT; + +template<typename ILhs, typename IRhs> +friend bool operator<(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT; + +private: +It it; +}; + +template<typename ILhs, typename IRhs> +[[nodiscard]] std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it - rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator==(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it == rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator!=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator<(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it < rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator>(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator<=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs > rhs); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator>=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +template<typename It> +class dense_set_local_iterator final { +template<typename> +friend class dense_set_local_iterator; + +public: +using value_type = typename It::value_type::second_type; +using pointer = const value_type *; +using reference = const value_type &; +using difference_type = std::ptrdiff_t; +using iterator_category = std::forward_iterator_tag; + +dense_set_local_iterator() ENTT_NOEXCEPT +: it{}, +offset{} {} + +dense_set_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT +: it{iter}, +offset{pos} {} + +template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> +dense_set_local_iterator(const dense_set_local_iterator<Other> &other) ENTT_NOEXCEPT +: it{other.it}, +offset{other.offset} {} + +dense_set_local_iterator &operator++() ENTT_NOEXCEPT { +return offset = it[offset].first, *this; +} + +dense_set_local_iterator operator++(int) ENTT_NOEXCEPT { +dense_set_local_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return std::addressof(it[offset].second); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return *operator->(); +} + +[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { +return offset; +} + +private: +It it; +std::size_t offset; +}; + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator==(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.index() == rhs.index(); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator!=(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Associative container for unique objects of a given type. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on its hash. Elements with the same hash code + * appear in the same bucket. + * + * @tparam Type Value type of the associative container. + * @tparam Hash Type of function to use to hash the values. + * @tparam KeyEqual Type of function to use to compare the values for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Type, typename Hash, typename KeyEqual, typename Allocator> +class dense_set { +static constexpr float default_threshold = 0.875f; +static constexpr std::size_t minimum_capacity = 8u; + +using node_type = std::pair<std::size_t, Type>; +using alloc_traits = std::allocator_traits<Allocator>; +static_assert(std::is_same_v<typename alloc_traits::value_type, 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>>; + +template<typename Other> +[[nodiscard]] std::size_t value_to_bucket(const Other &value) const ENTT_NOEXCEPT { +return fast_mod(sparse.second()(value), bucket_count()); +} + +template<typename Other> +[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) { +for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { +if(packed.second()(*it, value)) { +return begin() + static_cast<typename iterator::difference_type>(it.index()); +} +} + +return end(); +} + +template<typename Other> +[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const { +for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { +if(packed.second()(*it, value)) { +return cbegin() + static_cast<typename iterator::difference_type>(it.index()); +} +} + +return cend(); +} + +template<typename Other> +[[nodiscard]] auto insert_or_do_nothing(Other &&value) { +const auto index = value_to_bucket(value); + +if(auto it = constrained_find(value, index); it != end()) { +return std::make_pair(it, false); +} + +packed.first().emplace_back(sparse.first()[index], std::forward<Other>(value)); +sparse.first()[index] = packed.first().size() - 1u; +rehash_if_required(); + +return std::make_pair(--end(), true); +} + +void move_and_pop(const std::size_t pos) { +if(const auto last = size() - 1u; pos != last) { +packed.first()[pos] = std::move(packed.first().back()); +size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); +for(; *curr != last; curr = &packed.first()[*curr].first) {} +*curr = pos; +} + +packed.first().pop_back(); +} + +void rehash_if_required() { +if(size() > (bucket_count() * max_load_factor())) { +rehash(bucket_count() * 2u); +} +} + +public: +/*! @brief Key type of the container. */ +using key_type = Type; +/*! @brief Value type of the container. */ +using value_type = Type; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Type of function to use to hash the elements. */ +using hasher = Hash; +/*! @brief Type of function to use to compare the elements for equality. */ +using key_equal = KeyEqual; +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Random access iterator type. */ +using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>; +/*! @brief Constant random access iterator type. */ +using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_iterator>; +/*! @brief Forward iterator type. */ +using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::iterator>; +/*! @brief Constant forward iterator type. */ +using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_iterator>; + +/*! @brief Default constructor. */ +dense_set() +: dense_set(minimum_capacity) {} + +/** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ +explicit dense_set(const allocator_type &allocator) +: dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param allocator The allocator to use. + */ +dense_set(const size_type bucket_count, const allocator_type &allocator) +: dense_set{bucket_count, hasher{}, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ +dense_set(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) +: dense_set{bucket_count, hash, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ +explicit dense_set(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) +: sparse{allocator, hash}, +packed{allocator, equal}, +threshold{default_threshold} { +rehash(bucket_count); +} + +/*! @brief Default copy constructor. */ +dense_set(const dense_set &) = default; + +/** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ +dense_set(const dense_set &other, const allocator_type &allocator) +: sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, +packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, +threshold{other.threshold} {} + +/*! @brief Default move constructor. */ +dense_set(dense_set &&) = default; + +/** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ +dense_set(dense_set &&other, const allocator_type &allocator) +: sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, +packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, +threshold{other.threshold} {} + +/** + * @brief Default copy assignment operator. + * @return This container. + */ +dense_set &operator=(const dense_set &) = default; + +/** + * @brief Default move assignment operator. + * @return This container. + */ +dense_set &operator=(dense_set &&) = default; + +/** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ +[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { +return sparse.first().get_allocator(); +} + +/** + * @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. + */ +[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { +return packed.first().begin(); +} + +/*! @copydoc cbegin */ +[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { +return cbegin(); +} + +/*! @copydoc begin */ +[[nodiscard]] iterator begin() ENTT_NOEXCEPT { +return packed.first().begin(); +} + +/** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the internal array. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the element following the last instance of the + * internal array. + */ +[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { +return packed.first().end(); +} + +/*! @copydoc cend */ +[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { +return cend(); +} + +/*! @copydoc end */ +[[nodiscard]] iterator end() ENTT_NOEXCEPT { +return packed.first().end(); +} + +/** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return packed.first().empty(); +} + +/** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return packed.first().size(); +} + +/*! @brief Clears the container. */ +void clear() ENTT_NOEXCEPT { +sparse.first().clear(); +packed.first().clear(); +rehash(0u); +} + +/** + * @brief Inserts an element into the container, if it does not exist. + * @param value An element to insert into the container. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +std::pair<iterator, bool> insert(const value_type &value) { +return insert_or_do_nothing(value); +} + +/*! @copydoc insert */ +std::pair<iterator, bool> insert(value_type &&value) { +return insert_or_do_nothing(std::move(value)); +} + +/** + * @brief Inserts elements into the container, if they do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ +template<typename It> +void insert(It first, It last) { +for(; first != last; ++first) { +insert(*first); +} +} + +/** + * @brief Constructs an element in-place, if it does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +template<typename... Args> +std::pair<iterator, bool> emplace(Args &&...args) { +if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, value_type>)) { +return insert_or_do_nothing(std::forward<Args>(args)...); +} else { +auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward<Args>(args)...)); +const auto index = value_to_bucket(node.second); + +if(auto it = constrained_find(node.second, index); it != end()) { +packed.first().pop_back(); +return std::make_pair(it, false); +} + +std::swap(node.first, sparse.first()[index]); +rehash_if_required(); + +return std::make_pair(--end(), true); +} +} + +/** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ +iterator erase(const_iterator pos) { +const auto diff = pos - cbegin(); +erase(*pos); +return begin() + diff; +} + +/** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ +iterator erase(const_iterator first, const_iterator last) { +const auto dist = first - cbegin(); + +for(auto from = last - cbegin(); from != dist; --from) { +erase(packed.first()[from - 1u].second); +} + +return (begin() + dist); +} + +/** + * @brief Removes the element associated with a given value. + * @param value Value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ +size_type erase(const value_type &value) { +for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].first) { +if(packed.second()(packed.first()[*curr].second, value)) { +const auto index = *curr; +*curr = packed.first()[*curr].first; +move_and_pop(index); +return 1u; +} +} + +return 0u; +} + +/** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ +void swap(dense_set &other) { +using std::swap; +swap(sparse, other.sparse); +swap(packed, other.packed); +swap(threshold, other.threshold); +} + +/** + * @brief Finds an element with a given value. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ +[[nodiscard]] iterator find(const value_type &value) { +return constrained_find(value, value_to_bucket(value)); +} + +/*! @copydoc find */ +[[nodiscard]] const_iterator find(const value_type &value) const { +return constrained_find(value, value_to_bucket(value)); +} + +/** + * @brief Finds an element that compares _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>> +find(const Other &value) { +return constrained_find(value, value_to_bucket(value)); +} + +/*! @copydoc find */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>> +find(const Other &value) const { +return constrained_find(value, value_to_bucket(value)); +} + +/** + * @brief Checks if the container contains an element with a given value. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ +[[nodiscard]] bool contains(const value_type &value) const { +return (find(value) != cend()); +} + +/** + * @brief Checks if the container contains an element that compares + * _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>> +contains(const Other &value) const { +return (find(value) != cend()); +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] const_local_iterator cbegin(const size_type index) const { +return {packed.first().begin(), sparse.first()[index]}; +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] const_local_iterator begin(const size_type index) const { +return cbegin(index); +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] local_iterator begin(const size_type index) { +return {packed.first().begin(), sparse.first()[index]}; +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { +return {packed.first().begin(), (std::numeric_limits<size_type>::max)()}; +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const { +return cend(index); +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { +return {packed.first().begin(), (std::numeric_limits<size_type>::max)()}; +} + +/** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ +[[nodiscard]] size_type bucket_count() const { +return sparse.first().size(); +} + +/** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ +[[nodiscard]] size_type max_bucket_count() const { +return sparse.first().max_size(); +} + +/** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ +[[nodiscard]] size_type bucket_size(const size_type index) const { +return static_cast<size_type>(std::distance(begin(index), end(index))); +} + +/** + * @brief Returns the bucket for a given element. + * @param value The value of the element to examine. + * @return The bucket for the given element. + */ +[[nodiscard]] size_type bucket(const value_type &value) const { +return value_to_bucket(value); +} + +/** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ +[[nodiscard]] float load_factor() const { +return size() / static_cast<float>(bucket_count()); +} + +/** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ +[[nodiscard]] float max_load_factor() const { +return threshold; +} + +/** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ +void max_load_factor(const float value) { +ENTT_ASSERT(value > 0.f, "Invalid load factor"); +threshold = value; +rehash(0u); +} + +/** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param count New number of buckets. + */ +void rehash(const size_type count) { +auto value = (std::max)(count, minimum_capacity); +value = (std::max)(value, static_cast<size_type>(size() / max_load_factor())); + +if(const auto sz = next_power_of_two(value); sz != bucket_count()) { +sparse.first().resize(sz); +std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits<size_type>::max)()); + +for(size_type pos{}, last = size(); pos < last; ++pos) { +const auto index = value_to_bucket(packed.first()[pos].second); +packed.first()[pos].first = std::exchange(sparse.first()[index], pos); +} +} +} + +/** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param count New number of elements. + */ +void reserve(const size_type count) { +packed.first().reserve(count); +rehash(static_cast<size_type>(std::ceil(count / max_load_factor()))); +} + +/** + * @brief Returns the function used to hash the elements. + * @return The function used to hash the elements. + */ +[[nodiscard]] hasher hash_function() const { +return sparse.second(); +} + +/** + * @brief Returns the function used to compare elements for equality. + * @return The function used to compare elements for equality. + */ +[[nodiscard]] key_equal key_eq() const { +return packed.second(); +} + +private: +compressed_pair<sparse_container_type, hasher> sparse; +compressed_pair<packed_container_type, key_equal> packed; +float threshold; +}; + +} // namespace entt + +#endif + +// #include "meta.hpp" +#ifndef ENTT_META_META_HPP +#define ENTT_META_META_HPP + +#include <cstddef> +#include <iterator> +#include <memory> +#include <type_traits> +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + +#include <cstddef> +#include <memory> +#include <type_traits> +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { +/*! @brief Indicates that this is a transparent function object. */ +using is_transparent = void; + +/** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ +template<class Type> +[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { +return std::forward<Type>(value); +} +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template<typename Type, typename Class> +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template<typename Func> +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template<class... Func> +struct overloaded: Func... { +using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template<class... Func> +overloaded(Func...) -> overloaded<Func...>; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template<class Func> +struct y_combinator { +/** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ +y_combinator(Func recursive) +: func{std::move(recursive)} {} + +/** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ +template<class... Args> +decltype(auto) operator()(Args &&...args) const { +return func(*this, std::forward<Args>(args)...); +} + +/*! @copydoc operator()() */ +template<class... Args> +decltype(auto) operator()(Args &&...args) { +return func(*this, std::forward<Args>(args)...); +} + +private: +Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include <cstdint> +#include <type_traits> +// #include "../config/config.h" + + +namespace entt { + +template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)> +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include <string_view> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#endif + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include <cstddef> +#include <cstdint> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename> +struct fnv1a_traits; + +template<> +struct fnv1a_traits<std::uint32_t> { +using type = std::uint32_t; +static constexpr std::uint32_t offset = 2166136261; +static constexpr std::uint32_t prime = 16777619; +}; + +template<> +struct fnv1a_traits<std::uint64_t> { +using type = std::uint64_t; +static constexpr std::uint64_t offset = 14695981039346656037ull; +static constexpr std::uint64_t prime = 1099511628211ull; +}; + +template<typename Char> +struct basic_hashed_string { +using value_type = Char; +using size_type = std::size_t; +using hash_type = id_type; + +const value_type *repr; +size_type length; +hash_type hash; +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers in the codebase while using their numeric + * counterparts at runtime.<br/> + * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +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>; + +struct const_wrapper { +// non-explicit constructor on purpose +constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {} +const Char *repr; +}; + +// Fowler–Noll–Vo hash function v. 1a - the good +[[nodiscard]] static constexpr auto helper(const Char *str) ENTT_NOEXCEPT { +base_type base{str, 0u, hs_traits::offset}; + +for(; str[base.length]; ++base.length) { +base.hash = (base.hash ^ static_cast<hs_traits::type>(str[base.length])) * hs_traits::prime; +} + +return base; +} + +// Fowler–Noll–Vo hash function v. 1a - the good +[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) ENTT_NOEXCEPT { +base_type base{str, len, hs_traits::offset}; + +for(size_type pos{}; pos < len; ++pos) { +base.hash = (base.hash ^ static_cast<hs_traits::type>(str[pos])) * hs_traits::prime; +} + +return base; +} + +public: +/*! @brief Character type. */ +using value_type = typename base_type::value_type; +/*! @brief Unsigned integer type. */ +using size_type = typename base_type::size_type; +/*! @brief Unsigned integer type. */ +using hash_type = typename base_type::hash_type; + +/** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ +[[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) ENTT_NOEXCEPT { +return basic_hashed_string{str, len}; +} + +/** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ +template<std::size_t N> +[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { +return basic_hashed_string{str}; +} + +/** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ +[[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { +return basic_hashed_string{wrapper}; +} + +/*! @brief Constructs an empty hashed string. */ +constexpr basic_hashed_string() ENTT_NOEXCEPT +: base_type{} {} + +/** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +constexpr basic_hashed_string(const value_type *str, const size_type len) ENTT_NOEXCEPT +: base_type{helper(str, len)} {} + +/** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template<std::size_t N> +constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT +: base_type{helper(str)} {} + +/** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ +explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT +: base_type{helper(wrapper.repr)} {} + +/** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ +[[nodiscard]] constexpr size_type size() const ENTT_NOEXCEPT { +return base_type::length; +} + +/** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ +[[nodiscard]] constexpr const value_type *data() const ENTT_NOEXCEPT { +return base_type::repr; +} + +/** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ +[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { +return base_type::hash; +} + +/*! @copydoc data */ +[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { +return data(); +} + +/** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ +[[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template<typename Char> +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template<typename Char, std::size_t N> +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>; + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string<char>; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string<wchar_t>; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT { +return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { +return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +struct ENTT_API type_index final { +[[nodiscard]] static id_type next() ENTT_NOEXCEPT { +static ENTT_MAYBE_ATOMIC(id_type) value{}; +return value++; +} +}; + +template<typename Type> +[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION +std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; +auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); +auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); +return value; +#else +return std::string_view{""}; +#endif +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { +constexpr auto value = stripped_type_name<Type>(); +return value; +} + +template<typename Type> +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { +static const auto value = stripped_type_name<Type>(); +return value; +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { +constexpr auto stripped = stripped_type_name<Type>(); +constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); +return value; +} + +template<typename Type> +[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { +static const auto value = [](const auto stripped) { +return hashed_string::value(stripped.data(), stripped.size()); +}(stripped_type_name<Type>()); +return value; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template<typename Type, typename = void> +struct ENTT_API type_index final { +/** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ +[[nodiscard]] static id_type value() ENTT_NOEXCEPT { +static const id_type value = internal::type_index::next(); +return value; +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template<typename Type, typename = void> +struct type_hash final { +/** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION +[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { +return internal::type_hash<Type>(0); +#else +[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { +return type_index<Type>::value(); +#endif +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template<typename Type, typename = void> +struct type_name final { +/** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ +[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { +return internal::type_name<Type>(0); +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { +return value(); +} +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { +/** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ +template<typename Type> +constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT +: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, +identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, +alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {} + +/** + * @brief Type index. + * @return Type index. + */ +[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT { +return seq; +} + +/** + * @brief Type hash. + * @return Type hash. + */ +[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { +return identifier; +} + +/** + * @brief Type name. + * @return Type name. + */ +[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT { +return alias; +} + +private: +id_type seq; +id_type identifier; +std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.<br/> + * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template<typename Type> +[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT { +if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) { +static type_info instance{std::in_place_type<Type>}; +return instance; +} else { +return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} +} + +/*! @copydoc type_id */ +template<typename Type> +[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT { +return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template<std::size_t Len, std::size_t Align> +class basic_any { +enum class operation : std::uint8_t { +copy, +move, +transfer, +assign, +destroy, +compare, +get +}; + +enum class policy : std::uint8_t { +owner, +ref, +cref +}; + +using storage_type = std::aligned_storage_t<Len + !Len, Align>; +using vtable_type = const void *(const operation, const basic_any &, const void *); + +template<typename Type> +static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>; + +template<typename Type> +static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] const void *other) { +static_assert(!std::is_same_v<Type, void> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type"); +const Type *element = nullptr; + +if constexpr(in_situ<Type>) { +element = value.owner() ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance); +} else { +element = static_cast<const Type *>(value.instance); +} + +switch(op) { +case operation::copy: +if constexpr(std::is_copy_constructible_v<Type>) { +static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element); +} +break; +case operation::move: +if constexpr(in_situ<Type>) { +if(value.owner()) { +return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))}; +} +} + +return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr)); +case operation::transfer: +if constexpr(std::is_move_assignable_v<Type>) { +*const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other))); +return other; +} +[[fallthrough]]; +case operation::assign: +if constexpr(std::is_copy_assignable_v<Type>) { +*const_cast<Type *>(element) = *static_cast<const Type *>(other); +return other; +} +break; +case operation::destroy: +if constexpr(in_situ<Type>) { +element->~Type(); +} else if constexpr(std::is_array_v<Type>) { +delete[] element; +} else { +delete element; +} +break; +case operation::compare: +if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) { +return *static_cast<const Type *>(element) == *static_cast<const Type *>(other) ? other : nullptr; +} else { +return (element == other) ? other : nullptr; +} +case operation::get: +return element; +} + +return nullptr; +} + +template<typename Type, typename... Args> +void initialize([[maybe_unused]] Args &&...args) { +if constexpr(!std::is_void_v<Type>) { +info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +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"); +mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref; +instance = (std::addressof(args), ...); +} else if constexpr(in_situ<Type>) { +if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) { +new(&storage) Type{std::forward<Args>(args)...}; +} else { +new(&storage) Type(std::forward<Args>(args)...); +} +} else { +if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) { +instance = new Type{std::forward<Args>(args)...}; +} else { +instance = new Type(std::forward<Args>(args)...); +} +} +} +} + +basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT +: instance{other.data()}, +info{other.info}, +vtable{other.vtable}, +mode{pol} {} + +public: +/*! @brief Size of the internal storage. */ +static constexpr auto length = Len; +/*! @brief Alignment requirement. */ +static constexpr auto alignment = Align; + +/*! @brief Default constructor. */ +constexpr basic_any() ENTT_NOEXCEPT +: instance{}, +info{&type_id<void>()}, +vtable{}, +mode{policy::owner} {} + +/** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ +template<typename Type, typename... Args> +explicit basic_any(std::in_place_type_t<Type>, Args &&...args) +: basic_any{} { +initialize<Type>(std::forward<Args>(args)...); +} + +/** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ +template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>> +basic_any(Type &&value) +: basic_any{} { +initialize<std::decay_t<Type>>(std::forward<Type>(value)); +} + +/** + * @brief Copy constructor. + * @param other The instance to copy from. + */ +basic_any(const basic_any &other) +: basic_any{} { +if(other.vtable) { +other.vtable(operation::copy, other, this); +} +} + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +basic_any(basic_any &&other) ENTT_NOEXCEPT +: instance{}, +info{other.info}, +vtable{other.vtable}, +mode{other.mode} { +if(other.vtable) { +other.vtable(operation::move, other, this); +} +} + +/*! @brief Frees the internal storage, whatever it means. */ +~basic_any() { +if(vtable && owner()) { +vtable(operation::destroy, *this, nullptr); +} +} + +/** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ +basic_any &operator=(const basic_any &other) { +reset(); + +if(other.vtable) { +other.vtable(operation::copy, other, this); +} + +return *this; +} + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This any object. + */ +basic_any &operator=(basic_any &&other) ENTT_NOEXCEPT { +reset(); + +if(other.vtable) { +other.vtable(operation::move, other, this); +info = other.info; +vtable = other.vtable; +mode = other.mode; +} + +return *this; +} + +/** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This any object. + */ +template<typename Type> +std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>, basic_any &> +operator=(Type &&value) { +emplace<std::decay_t<Type>>(std::forward<Type>(value)); +return *this; +} + +/** + * @brief Returns the object type if any, `type_id<void>()` otherwise. + * @return The object type if any, `type_id<void>()` otherwise. + */ +[[nodiscard]] const type_info &type() const ENTT_NOEXCEPT { +return *info; +} + +/** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ +[[nodiscard]] const void *data() const ENTT_NOEXCEPT { +return vtable ? vtable(operation::get, *this, nullptr) : nullptr; +} + +/** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ +[[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT { +return *info == req ? data() : nullptr; +} + +/** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ +[[nodiscard]] void *data() ENTT_NOEXCEPT { +return (!vtable || mode == policy::cref) ? nullptr : const_cast<void *>(vtable(operation::get, *this, nullptr)); +} + +/** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ +[[nodiscard]] void *data(const type_info &req) ENTT_NOEXCEPT { +return *info == req ? data() : nullptr; +} + +/** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ +template<typename Type, typename... Args> +void emplace(Args &&...args) { +reset(); +initialize<Type>(std::forward<Args>(args)...); +} + +/** + * @brief Assigns a value to the contained object without replacing it. + * @param other The value to assign to the contained object. + * @return True in case of success, false otherwise. + */ +bool assign(const any &other) { +if(vtable && mode != policy::cref && *info == *other.info) { +return (vtable(operation::assign, *this, other.data()) != nullptr); +} + +return false; +} + +/*! @copydoc assign */ +bool assign(any &&other) { +if(vtable && mode != policy::cref && *info == *other.info) { +if(auto *val = other.data(); val) { +return (vtable(operation::transfer, *this, val) != nullptr); +} else { +return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr); +} +} + +return false; +} + +/*! @brief Destroys contained object */ +void reset() { +if(vtable && owner()) { +vtable(operation::destroy, *this, nullptr); +} + +info = &type_id<void>(); +vtable = nullptr; +mode = policy::owner; +} + +/** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return vtable != nullptr; +} + +/** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ +bool operator==(const basic_any &other) const ENTT_NOEXCEPT { +if(vtable && *info == *other.info) { +return (vtable(operation::compare, *this, other.data()) != nullptr); +} + +return (!vtable && !other.vtable); +} + +/** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ +[[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT { +return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)}; +} + +/*! @copydoc as_ref */ +[[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT { +return basic_any{*this, policy::cref}; +} + +/** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ +[[nodiscard]] bool owner() const ENTT_NOEXCEPT { +return (mode == policy::owner); +} + +private: +union { +const void *instance; +storage_type storage; +}; +const type_info *info; +vtable_type *vtable; +policy mode; +}; + +/** + * @brief Checks if two wrappers differ in their content. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param lhs A wrapper, either empty or not. + * @param rhs A wrapper, either empty or not. + * @return True if the two wrappers differ in their content, false otherwise. + */ +template<std::size_t Len, std::size_t Align> +[[nodiscard]] inline bool operator!=(const basic_any<Len, Align> &lhs, const basic_any<Len, Align> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template<typename Type, std::size_t Len, std::size_t Align> +Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT { +const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); +ENTT_ASSERT(instance, "Invalid instance"); +return static_cast<Type>(*instance); +} + +/*! @copydoc any_cast */ +template<typename Type, std::size_t Len, std::size_t Align> +Type any_cast(basic_any<Len, Align> &data) ENTT_NOEXCEPT { +// forces const on non-reference types to make them work also with wrappers for const references +auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data); +ENTT_ASSERT(instance, "Invalid instance"); +return static_cast<Type>(*instance); +} + +/*! @copydoc any_cast */ +template<typename Type, std::size_t Len, std::size_t Align> +Type any_cast(basic_any<Len, Align> &&data) ENTT_NOEXCEPT { +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)); +} else { +return any_cast<Type>(data); +} +} else { +auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); +ENTT_ASSERT(instance, "Invalid instance"); +return static_cast<Type>(std::move(*instance)); +} +} + +/*! @copydoc any_cast */ +template<typename Type, std::size_t Len, std::size_t Align> +const Type *any_cast(const basic_any<Len, Align> *data) ENTT_NOEXCEPT { +const auto &info = type_id<std::remove_cv_t<std::remove_reference_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) ENTT_NOEXCEPT { +const auto &info = type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +// last attempt to make wrappers for const references return their values +return static_cast<Type *>(static_cast<constness_as_t<basic_any<Len, Align>, Type> *>(data)->data(info)); +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @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) { +return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @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) { +return basic_any<Len, Align>{std::in_place_type<std::conditional_t<std::is_rvalue_reference_v<Type>, std::decay_t<Type>, Type>>, std::forward<Type>(value)}; +} + +} // namespace entt + +#endif + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include <cstdint> +#include <type_traits> +// #include "../config/config.h" + + +namespace entt { + +template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)> +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include <iterator> +#include <memory> +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template<typename Type> +struct input_iterator_pointer final { +/*! @brief Pointer type. */ +using pointer = Type *; + +/*! @brief Default copy constructor, deleted on purpose. */ +input_iterator_pointer(const input_iterator_pointer &) = delete; + +/*! @brief Default move constructor. */ +input_iterator_pointer(input_iterator_pointer &&) = default; + +/** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ +input_iterator_pointer(Type &&val) +: value{std::move(val)} {} + +/** + * @brief Default copy assignment operator, deleted on purpose. + * @return This proxy object. + */ +input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; + +/** + * @brief Default move assignment operator. + * @return This proxy object. + */ +input_iterator_pointer &operator=(input_iterator_pointer &&) = default; + +/** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ +[[nodiscard]] pointer operator->() ENTT_NOEXCEPT { +return std::addressof(value); +} + +private: +Type value; +}; + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template<typename It, typename Sentinel = It> +struct iterable_adaptor final { +/*! @brief Value type. */ +using value_type = typename std::iterator_traits<It>::value_type; +/*! @brief Iterator type. */ +using iterator = It; +/*! @brief Sentinel type. */ +using sentinel = Sentinel; + +/*! @brief Default constructor. */ +iterable_adaptor() = default; + +/** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ +iterable_adaptor(iterator from, sentinel to) +: first{from}, +last{to} {} + +/** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return first; +} + +/** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ +[[nodiscard]] sentinel end() const ENTT_NOEXCEPT { +return last; +} + +/*! @copydoc begin */ +[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { +return begin(); +} + +/*! @copydoc end */ +[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT { +return end(); +} + +private: +It first; +Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include <string_view> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +struct ENTT_API type_index final { +[[nodiscard]] static id_type next() ENTT_NOEXCEPT { +static ENTT_MAYBE_ATOMIC(id_type) value{}; +return value++; +} +}; + +template<typename Type> +[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION +std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; +auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); +auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); +return value; +#else +return std::string_view{""}; +#endif +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { +constexpr auto value = stripped_type_name<Type>(); +return value; +} + +template<typename Type> +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { +static const auto value = stripped_type_name<Type>(); +return value; +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { +constexpr auto stripped = stripped_type_name<Type>(); +constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); +return value; +} + +template<typename Type> +[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { +static const auto value = [](const auto stripped) { +return hashed_string::value(stripped.data(), stripped.size()); +}(stripped_type_name<Type>()); +return value; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template<typename Type, typename = void> +struct ENTT_API type_index final { +/** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ +[[nodiscard]] static id_type value() ENTT_NOEXCEPT { +static const id_type value = internal::type_index::next(); +return value; +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template<typename Type, typename = void> +struct type_hash final { +/** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION +[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { +return internal::type_hash<Type>(0); +#else +[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { +return type_index<Type>::value(); +#endif +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template<typename Type, typename = void> +struct type_name final { +/** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ +[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { +return internal::type_name<Type>(0); +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { +return value(); +} +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { +/** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ +template<typename Type> +constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT +: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, +identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, +alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {} + +/** + * @brief Type index. + * @return Type index. + */ +[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT { +return seq; +} + +/** + * @brief Type hash. + * @return Type hash. + */ +[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { +return identifier; +} + +/** + * @brief Type name. + * @return Type name. + */ +[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT { +return alias; +} + +private: +id_type seq; +id_type identifier; +std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.<br/> + * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template<typename Type> +[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT { +if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) { +static type_info instance{std::in_place_type<Type>}; +return instance; +} else { +return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} +} + +/*! @copydoc type_id */ +template<typename Type> +[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT { +return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { +/*! @brief Indicates that this is a transparent function object. */ +using is_transparent = void; + +/** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ +template<class Type> +[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { +return std::forward<Type>(value); +} +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template<typename Type, typename Class> +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template<typename Func> +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template<class... Func> +struct overloaded: Func... { +using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template<class... Func> +overloaded(Func...) -> overloaded<Func...>; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template<class Func> +struct y_combinator { +/** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ +y_combinator(Func recursive) +: func{std::move(recursive)} {} + +/** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ +template<class... Args> +decltype(auto) operator()(Args &&...args) const { +return func(*this, std::forward<Args>(args)...); +} + +/*! @copydoc operator()() */ +template<class... Args> +decltype(auto) operator()(Args &&...args) { +return func(*this, std::forward<Args>(args)...); +} + +private: +Func func; +}; + +} // namespace entt + +#endif + +// #include "adl_pointer.hpp" +#ifndef ENTT_META_ADL_POINTER_HPP +#define ENTT_META_ADL_POINTER_HPP + +namespace entt { + +/** + * @brief ADL based lookup function for dereferencing meta pointer-like types. + * @tparam Type Element type. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ +template<typename Type> +decltype(auto) dereference_meta_pointer_like(const Type &value) { +return *value; +} + +/** + * @brief Fake ADL based lookup function for meta pointer-like types. + * @tparam Type Element type. + */ +template<typename Type> +struct adl_meta_pointer_like { +/** + * @brief Uses the default ADL based lookup method to resolve the call. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ +static decltype(auto) dereference(const Type &value) { +return dereference_meta_pointer_like(value); +} +}; + +} // namespace entt + +#endif + +// #include "ctx.hpp" +#ifndef ENTT_META_CTX_HPP +#define ENTT_META_CTX_HPP + +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +struct meta_type_node; + +struct ENTT_API meta_context { +// we could use the lines below but VS2017 returns with an ICE if combined with ENTT_API despite the code being valid C++ +// inline static meta_type_node *local = nullptr; +// inline static meta_type_node **global = &local; + +[[nodiscard]] static meta_type_node *&local() ENTT_NOEXCEPT { +static meta_type_node *chain = nullptr; +return chain; +} + +[[nodiscard]] static meta_type_node **&global() ENTT_NOEXCEPT { +static meta_type_node **chain = &local(); +return chain; +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @brief Opaque container for a meta context. */ +struct meta_ctx { +/** + * @brief Binds the meta system to a given context. + * @param other A valid context to which to bind. + */ +static void bind(meta_ctx other) ENTT_NOEXCEPT { +internal::meta_context::global() = other.ctx; +} + +private: +internal::meta_type_node **ctx{&internal::meta_context::local()}; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_META_FWD_HPP +#define ENTT_META_FWD_HPP + +namespace entt { + +class meta_sequence_container; + +class meta_associative_container; + +class meta_any; + +struct meta_handle; + +struct meta_prop; + +struct meta_data; + +struct meta_func; + +class meta_type; + +} // namespace entt + +#endif + +// #include "node.hpp" +#ifndef ENTT_META_NODE_HPP +#define ENTT_META_NODE_HPP + +#include <cstddef> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "../core/enum.hpp" +#ifndef ENTT_CORE_ENUM_HPP +#define ENTT_CORE_ENUM_HPP + +#include <type_traits> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Enable bitmask support for enum classes. + * @tparam Type The enum type for which to enable bitmask support. + */ +template<typename Type, typename = void> +struct enum_as_bitmask: std::false_type {}; + +/*! @copydoc enum_as_bitmask */ +template<typename Type> +struct enum_as_bitmask<Type, std::void_t<decltype(Type::_entt_enum_as_bitmask)>>: std::is_enum<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The enum class type for which to enable bitmask support. + */ +template<typename Type> +inline constexpr bool enum_as_bitmask_v = enum_as_bitmask<Type>::value; + +} // namespace entt + +/** + * @brief Operator available for enums for which bitmask support is enabled. + * @tparam Type Enum class type. + * @param lhs The first value to use. + * @param rhs The second value to use. + * @return The result of invoking the operator on the underlying types of the + * two values provided. + */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type> +operator|(const Type lhs, const Type rhs) ENTT_NOEXCEPT { +return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) | static_cast<std::underlying_type_t<Type>>(rhs)); +} + +/*! @copydoc operator| */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type> +operator&(const Type lhs, const Type rhs) ENTT_NOEXCEPT { +return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) & static_cast<std::underlying_type_t<Type>>(rhs)); +} + +/*! @copydoc operator| */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type> +operator^(const Type lhs, const Type rhs) ENTT_NOEXCEPT { +return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) ^ static_cast<std::underlying_type_t<Type>>(rhs)); +} + +/** + * @brief Operator available for enums for which bitmask support is enabled. + * @tparam Type Enum class type. + * @param value The value to use. + * @return The result of invoking the operator on the underlying types of the + * value provided. + */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type> +operator~(const Type value) ENTT_NOEXCEPT { +return static_cast<Type>(~static_cast<std::underlying_type_t<Type>>(value)); +} + +/*! @copydoc operator~ */ +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, bool> +operator!(const Type value) ENTT_NOEXCEPT { +return !static_cast<std::underlying_type_t<Type>>(value); +} + +/*! @copydoc operator| */ +template<typename Type> +constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &> +operator|=(Type &lhs, const Type rhs) ENTT_NOEXCEPT { +return (lhs = (lhs | rhs)); +} + +/*! @copydoc operator| */ +template<typename Type> +constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &> +operator&=(Type &lhs, const Type rhs) ENTT_NOEXCEPT { +return (lhs = (lhs & rhs)); +} + +/*! @copydoc operator| */ +template<typename Type> +constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &> +operator^=(Type &lhs, const Type rhs) ENTT_NOEXCEPT { +return (lhs = (lhs ^ rhs)); +} + +#endif + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "type_traits.hpp" +#ifndef ENTT_META_TYPE_TRAITS_HPP +#define ENTT_META_TYPE_TRAITS_HPP + +#include <type_traits> +#include <utility> + +namespace entt { + +/** + * @brief Traits class template to be specialized to enable support for meta + * template information. + */ +template<typename> +struct meta_template_traits; + +/** + * @brief Traits class template to be specialized to enable support for meta + * sequence containers. + */ +template<typename> +struct meta_sequence_container_traits; + +/** + * @brief Traits class template to be specialized to enable support for meta + * associative containers. + */ +template<typename> +struct meta_associative_container_traits; + +/** + * @brief Provides the member constant `value` to true if a given type is a + * pointer-like type from the point of view of the meta system, false otherwise. + * @tparam Type Potentially pointer-like type. + */ +template<typename> +struct is_meta_pointer_like: std::false_type {}; + +/** + * @brief Partial specialization to ensure that const pointer-like types are + * also accepted. + * @tparam Type Potentially pointer-like type. + */ +template<typename Type> +struct is_meta_pointer_like<const Type>: is_meta_pointer_like<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type Potentially pointer-like type. + */ +template<typename Type> +inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like<Type>::value; + +} // namespace entt + +#endif + + +namespace entt { + +class meta_any; +class meta_type; +struct meta_handle; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +enum class meta_traits : std::uint32_t { +is_none = 0x0000, +is_const = 0x0001, +is_static = 0x0002, +is_arithmetic = 0x0004, +is_array = 0x0008, +is_enum = 0x0010, +is_class = 0x0020, +is_pointer = 0x0040, +is_meta_pointer_like = 0x0080, +is_meta_sequence_container = 0x0100, +is_meta_associative_container = 0x0200, +_entt_enum_as_bitmask +}; + +struct meta_type_node; + +struct meta_prop_node { +meta_prop_node *next; +const meta_any &id; +meta_any &value; +}; + +struct meta_base_node { +meta_base_node *next; +meta_type_node *const type; +meta_any (*const cast)(meta_any) ENTT_NOEXCEPT; +}; + +struct meta_conv_node { +meta_conv_node *next; +meta_type_node *const type; +meta_any (*const conv)(const meta_any &); +}; + +struct meta_ctor_node { +using size_type = std::size_t; +meta_ctor_node *next; +const size_type arity; +meta_type (*const arg)(const size_type) ENTT_NOEXCEPT; +meta_any (*const invoke)(meta_any *const); +}; + +struct meta_data_node { +using size_type = std::size_t; +id_type id; +const meta_traits traits; +meta_data_node *next; +meta_prop_node *prop; +const size_type arity; +meta_type_node *const type; +meta_type (*const arg)(const size_type) ENTT_NOEXCEPT; +bool (*const set)(meta_handle, meta_any); +meta_any (*const get)(meta_handle); +}; + +struct meta_func_node { +using size_type = std::size_t; +id_type id; +const meta_traits traits; +meta_func_node *next; +meta_prop_node *prop; +const size_type arity; +meta_type_node *const ret; +meta_type (*const arg)(const size_type) ENTT_NOEXCEPT; +meta_any (*const invoke)(meta_handle, meta_any *const); +}; + +struct meta_template_node { +using size_type = std::size_t; +const size_type arity; +meta_type_node *const type; +meta_type_node *(*const arg)(const size_type)ENTT_NOEXCEPT; +}; + +struct meta_type_node { +using size_type = std::size_t; +const type_info *info; +id_type id; +const meta_traits traits; +meta_type_node *next; +meta_prop_node *prop; +const size_type size_of; +meta_any (*const default_constructor)(); +double (*const conversion_helper)(void *, const void *); +const meta_template_node *const templ; +meta_ctor_node *ctor{nullptr}; +meta_base_node *base{nullptr}; +meta_conv_node *conv{nullptr}; +meta_data_node *data{nullptr}; +meta_func_node *func{nullptr}; +void (*dtor)(void *){nullptr}; +}; + +template<typename... Args> +meta_type_node *meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT; + +template<typename Type> +class ENTT_API meta_node { +static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Invalid type"); + +[[nodiscard]] static auto *meta_default_constructor() ENTT_NOEXCEPT { +if constexpr(std::is_default_constructible_v<Type>) { +return +[]() { return meta_any{std::in_place_type<Type>}; }; +} else { +return static_cast<std::decay_t<decltype(meta_type_node::default_constructor)>>(nullptr); +} +} + +[[nodiscard]] static auto *meta_conversion_helper() ENTT_NOEXCEPT { +if constexpr(std::is_arithmetic_v<Type>) { +return +[](void *bin, const void *value) { +return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(*static_cast<const double *>(value))) : static_cast<double>(*static_cast<const Type *>(value)); +}; +} else if constexpr(std::is_enum_v<Type>) { +return +[](void *bin, const void *value) { +return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(static_cast<std::underlying_type_t<Type>>(*static_cast<const double *>(value)))) : static_cast<double>(*static_cast<const Type *>(value)); +}; +} else { +return static_cast<std::decay_t<decltype(meta_type_node::conversion_helper)>>(nullptr); +} +} + +[[nodiscard]] static meta_template_node *meta_template_info() ENTT_NOEXCEPT { +if constexpr(is_complete_v<meta_template_traits<Type>>) { +static meta_template_node node{ +meta_template_traits<Type>::args_type::size, +meta_node<typename meta_template_traits<Type>::class_type>::resolve(), +[](const std::size_t index) ENTT_NOEXCEPT { return meta_arg_node(typename meta_template_traits<Type>::args_type{}, index); } +// tricks clang-format +}; + +return &node; +} else { +return nullptr; +} +} + +public: +[[nodiscard]] static meta_type_node *resolve() ENTT_NOEXCEPT { +static meta_type_node node{ +&type_id<Type>(), +{}, +internal::meta_traits::is_none +| (std::is_arithmetic_v<Type> ? internal::meta_traits::is_arithmetic : internal::meta_traits::is_none) +| (std::is_array_v<Type> ? internal::meta_traits::is_array : internal::meta_traits::is_none) +| (std::is_enum_v<Type> ? internal::meta_traits::is_enum : internal::meta_traits::is_none) +| (std::is_class_v<Type> ? internal::meta_traits::is_class : internal::meta_traits::is_none) +| (std::is_pointer_v<Type> ? internal::meta_traits::is_pointer : internal::meta_traits::is_none) +| (is_meta_pointer_like_v<Type> ? internal::meta_traits::is_meta_pointer_like : internal::meta_traits::is_none) +| (is_complete_v<meta_sequence_container_traits<Type>> ? internal::meta_traits::is_meta_sequence_container : internal::meta_traits::is_none) +| (is_complete_v<meta_associative_container_traits<Type>> ? internal::meta_traits::is_meta_associative_container : internal::meta_traits::is_none), +nullptr, +nullptr, +size_of_v<Type>, +meta_default_constructor(), +meta_conversion_helper(), +meta_template_info() +// tricks clang-format +}; + +return &node; +} +}; + +template<typename... Args> +[[nodiscard]] meta_type_node *meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT { +meta_type_node *args[sizeof...(Args) + 1u]{nullptr, internal::meta_node<std::remove_cv_t<std::remove_reference_t<Args>>>::resolve()...}; +return args[index + 1u]; +} + +template<auto Member, typename Type> +[[nodiscard]] static std::decay_t<decltype(std::declval<internal::meta_type_node>().*Member)> find_by(const Type &info_or_id, const internal::meta_type_node *node) ENTT_NOEXCEPT { +for(auto *curr = node->*Member; curr; curr = curr->next) { +if constexpr(std::is_same_v<Type, type_info>) { +if(*curr->type->info == info_or_id) { +return curr; +} +} else if constexpr(std::is_same_v<decltype(curr), meta_base_node *>) { +if(curr->type->id == info_or_id) { +return curr; +} +} else { +if(curr->id == info_or_id) { +return curr; +} +} +} + +for(auto *curr = node->base; curr; curr = curr->next) { +if(auto *ret = find_by<Member>(info_or_id, curr->type); ret) { +return ret; +} +} + +return nullptr; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +} // namespace entt + +#endif + +// #include "range.hpp" +#ifndef ENTT_META_RANGE_HPP +#define ENTT_META_RANGE_HPP + +#include <cstddef> +#include <iterator> +// #include "../core/iterator.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type, typename Node> +struct meta_range_iterator final { +using difference_type = std::ptrdiff_t; +using value_type = Type; +using pointer = input_iterator_pointer<value_type>; +using reference = value_type; +using iterator_category = std::input_iterator_tag; +using node_type = Node; + +meta_range_iterator() ENTT_NOEXCEPT +: it{} {} + +meta_range_iterator(node_type *head) ENTT_NOEXCEPT +: it{head} {} + +meta_range_iterator &operator++() ENTT_NOEXCEPT { +return (it = it->next), *this; +} + +meta_range_iterator operator++(int) ENTT_NOEXCEPT { +meta_range_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return it; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +[[nodiscard]] bool operator==(const meta_range_iterator &other) const ENTT_NOEXCEPT { +return it == other.it; +} + +[[nodiscard]] bool operator!=(const meta_range_iterator &other) const ENTT_NOEXCEPT { +return !(*this == other); +} + +private: +node_type *it; +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Iterable range to use to iterate all types of meta objects. + * @tparam Type Type of meta objects returned. + * @tparam Node Type of meta nodes iterated. + */ +template<typename Type, typename Node = typename Type::node_type> +struct meta_range final { +/*! @brief Node type. */ +using node_type = Node; +/*! @brief Input iterator type. */ +using iterator = internal::meta_range_iterator<Type, Node>; +/*! @brief Constant input iterator type. */ +using const_iterator = iterator; + +/*! @brief Default constructor. */ +meta_range() ENTT_NOEXCEPT = default; + +/** + * @brief Constructs a meta range from a given node. + * @param head The underlying node with which to construct the range. + */ +meta_range(node_type *head) ENTT_NOEXCEPT +: node{head} {} + +/** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first meta object of the range. + */ +[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { +return iterator{node}; +} + +/*! @copydoc cbegin */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return cbegin(); +} + +/** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last meta object of the + * range. + */ +[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { +return iterator{}; +} + +/*! @copydoc cend */ +[[nodiscard]] iterator end() const ENTT_NOEXCEPT { +return cend(); +} + +private: +node_type *node{nullptr}; +}; + +} // namespace entt + +#endif + +// #include "type_traits.hpp" + + +namespace entt { + +class meta_any; +class meta_type; + +/*! @brief Proxy object for sequence containers. */ +class meta_sequence_container { +class meta_iterator; + +public: +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Meta iterator type. */ +using iterator = meta_iterator; + +/*! @brief Default constructor. */ +meta_sequence_container() ENTT_NOEXCEPT = default; + +/** + * @brief Construct a proxy object for sequence containers. + * @tparam Type Type of container to wrap. + * @param instance The container to wrap. + */ +template<typename Type> +meta_sequence_container(std::in_place_type_t<Type>, any instance) ENTT_NOEXCEPT +: value_type_node{internal::meta_node<std::remove_cv_t<std::remove_reference_t<typename Type::value_type>>>::resolve()}, +size_fn{&meta_sequence_container_traits<Type>::size}, +resize_fn{&meta_sequence_container_traits<Type>::resize}, +iter_fn{&meta_sequence_container_traits<Type>::iter}, +insert_fn{&meta_sequence_container_traits<Type>::insert}, +erase_fn{&meta_sequence_container_traits<Type>::erase}, +storage{std::move(instance)} {} + +[[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; +[[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; +inline bool resize(const size_type); +inline bool clear(); +[[nodiscard]] inline iterator begin(); +[[nodiscard]] inline iterator end(); +inline iterator insert(iterator, meta_any); +inline iterator erase(iterator); +[[nodiscard]] inline meta_any operator[](const size_type); +[[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; + +private: +internal::meta_type_node *value_type_node = nullptr; +size_type (*size_fn)(const any &) ENTT_NOEXCEPT = nullptr; +bool (*resize_fn)(any &, size_type) = nullptr; +iterator (*iter_fn)(any &, const bool) = nullptr; +iterator (*insert_fn)(any &, const std::ptrdiff_t, meta_any &) = nullptr; +iterator (*erase_fn)(any &, const std::ptrdiff_t) = nullptr; +any storage{}; +}; + +/*! @brief Proxy object for associative containers. */ +class meta_associative_container { +class meta_iterator; + +public: +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Meta iterator type. */ +using iterator = meta_iterator; + +/*! @brief Default constructor. */ +meta_associative_container() ENTT_NOEXCEPT = default; + +/** + * @brief Construct a proxy object for associative containers. + * @tparam Type Type of container to wrap. + * @param instance The container to wrap. + */ +template<typename Type> +meta_associative_container(std::in_place_type_t<Type>, any instance) ENTT_NOEXCEPT +: key_only_container{meta_associative_container_traits<Type>::key_only}, +key_type_node{internal::meta_node<std::remove_cv_t<std::remove_reference_t<typename Type::key_type>>>::resolve()}, +mapped_type_node{nullptr}, +value_type_node{internal::meta_node<std::remove_cv_t<std::remove_reference_t<typename Type::value_type>>>::resolve()}, +size_fn{&meta_associative_container_traits<Type>::size}, +clear_fn{&meta_associative_container_traits<Type>::clear}, +iter_fn{&meta_associative_container_traits<Type>::iter}, +insert_fn{&meta_associative_container_traits<Type>::insert}, +erase_fn{&meta_associative_container_traits<Type>::erase}, +find_fn{&meta_associative_container_traits<Type>::find}, +storage{std::move(instance)} { +if constexpr(!meta_associative_container_traits<Type>::key_only) { +mapped_type_node = internal::meta_node<std::remove_cv_t<std::remove_reference_t<typename Type::mapped_type>>>::resolve(); +} +} + +[[nodiscard]] inline bool key_only() const ENTT_NOEXCEPT; +[[nodiscard]] inline meta_type key_type() const ENTT_NOEXCEPT; +[[nodiscard]] inline meta_type mapped_type() const ENTT_NOEXCEPT; +[[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; +[[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; +inline bool clear(); +[[nodiscard]] inline iterator begin(); +[[nodiscard]] inline iterator end(); +inline bool insert(meta_any, meta_any); +inline bool erase(meta_any); +[[nodiscard]] inline iterator find(meta_any); +[[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; + +private: +bool key_only_container{}; +internal::meta_type_node *key_type_node = nullptr; +internal::meta_type_node *mapped_type_node = nullptr; +internal::meta_type_node *value_type_node = nullptr; +size_type (*size_fn)(const any &) ENTT_NOEXCEPT = nullptr; +bool (*clear_fn)(any &) = nullptr; +iterator (*iter_fn)(any &, const bool) = nullptr; +bool (*insert_fn)(any &, meta_any &, meta_any &) = nullptr; +bool (*erase_fn)(any &, meta_any &) = nullptr; +iterator (*find_fn)(any &, meta_any &) = nullptr; +any storage{}; +}; + +/*! @brief Opaque wrapper for values of any type. */ +class meta_any { +enum class operation : std::uint8_t { +deref, +seq, +assoc +}; + +using vtable_type = void(const operation, const any &, void *); + +template<typename Type> +static void basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const any &value, [[maybe_unused]] void *other) { +static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type"); + +if constexpr(!std::is_void_v<Type>) { +switch(op) { +case operation::deref: +if constexpr(is_meta_pointer_like_v<Type>) { +if constexpr(std::is_function_v<std::remove_const_t<typename std::pointer_traits<Type>::element_type>>) { +*static_cast<meta_any *>(other) = any_cast<Type>(value); +} else if constexpr(!std::is_same_v<std::remove_const_t<typename std::pointer_traits<Type>::element_type>, void>) { +using in_place_type = decltype(adl_meta_pointer_like<Type>::dereference(any_cast<const Type &>(value))); + +if constexpr(std::is_constructible_v<bool, Type>) { +if(const auto &pointer_like = any_cast<const Type &>(value); pointer_like) { +static_cast<meta_any *>(other)->emplace<in_place_type>(adl_meta_pointer_like<Type>::dereference(pointer_like)); +} +} else { +static_cast<meta_any *>(other)->emplace<in_place_type>(adl_meta_pointer_like<Type>::dereference(any_cast<const Type &>(value))); +} +} +} +break; +case operation::seq: +if constexpr(is_complete_v<meta_sequence_container_traits<Type>>) { +*static_cast<meta_sequence_container *>(other) = {std::in_place_type<Type>, std::move(const_cast<any &>(value))}; +} +break; +case operation::assoc: +if constexpr(is_complete_v<meta_associative_container_traits<Type>>) { +*static_cast<meta_associative_container *>(other) = {std::in_place_type<Type>, std::move(const_cast<any &>(value))}; +} +break; +} +} +} + +void release() { +if(node && node->dtor && storage.owner()) { +node->dtor(storage.data()); +} +} + +meta_any(const meta_any &other, any ref) ENTT_NOEXCEPT +: storage{std::move(ref)}, +node{storage ? other.node : nullptr}, +vtable{storage ? other.vtable : &basic_vtable<void>} {} + +public: +/*! @brief Default constructor. */ +meta_any() ENTT_NOEXCEPT +: storage{}, +node{}, +vtable{&basic_vtable<void>} {} + +/** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ +template<typename Type, typename... Args> +explicit meta_any(std::in_place_type_t<Type>, Args &&...args) +: storage{std::in_place_type<Type>, std::forward<Args>(args)...}, +node{internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve()}, +vtable{&basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>} {} + +/** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ +template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>>> +meta_any(Type &&value) +: meta_any{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)} {} + +/** + * @brief Copy constructor. + * @param other The instance to copy from. + */ +meta_any(const meta_any &other) = default; + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +meta_any(meta_any &&other) ENTT_NOEXCEPT +: storage{std::move(other.storage)}, +node{std::exchange(other.node, nullptr)}, +vtable{std::exchange(other.vtable, &basic_vtable<void>)} {} + +/*! @brief Frees the internal storage, whatever it means. */ +~meta_any() { +release(); +} + +/** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This meta any object. + */ +meta_any &operator=(const meta_any &other) { +release(); +vtable = other.vtable; +storage = other.storage; +node = other.node; +return *this; +} + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This meta any object. + */ +meta_any &operator=(meta_any &&other) ENTT_NOEXCEPT { +release(); +vtable = std::exchange(other.vtable, &basic_vtable<void>); +storage = std::move(other.storage); +node = std::exchange(other.node, nullptr); +return *this; +} + +/** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This meta any object. + */ +template<typename Type> +std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>, meta_any &> +operator=(Type &&value) { +emplace<std::decay_t<Type>>(std::forward<Type>(value)); +return *this; +} + +/*! @copydoc any::type */ +[[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; + +/*! @copydoc any::data */ +[[nodiscard]] const void *data() const ENTT_NOEXCEPT { +return storage.data(); +} + +/*! @copydoc any::data */ +[[nodiscard]] void *data() ENTT_NOEXCEPT { +return storage.data(); +} + +/** + * @brief Invokes the underlying function, if possible. + * + * @sa meta_func::invoke + * + * @tparam Args Types of arguments to use to invoke the function. + * @param id Unique identifier. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ +template<typename... Args> +meta_any invoke(const id_type id, Args &&...args) const; + +/*! @copydoc invoke */ +template<typename... Args> +meta_any invoke(const id_type id, Args &&...args); + +/** + * @brief Sets the value of a given variable. + * + * The type of the value is such that a cast or conversion to the type of + * the variable is possible. Otherwise, invoking the setter does nothing. + * + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ +template<typename Type> +bool set(const id_type id, Type &&value); + +/** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @return A wrapper containing the value of the underlying variable. + */ +[[nodiscard]] meta_any get(const id_type id) const; + +/*! @copydoc get */ +[[nodiscard]] meta_any get(const id_type id); + +/** + * @brief Tries to cast an instance to a given type. + * @tparam Type Type to which to cast the instance. + * @return A (possibly null) pointer to the contained instance. + */ +template<typename Type> +[[nodiscard]] const Type *try_cast() const { +if(const auto &info = type_id<Type>(); node && *node->info == info) { +return any_cast<Type>(&storage); +} else if(node) { +for(auto *it = node->base; it; it = it->next) { +const auto as_const = it->cast(as_ref()); + +if(const Type *base = as_const.template try_cast<Type>(); base) { +return base; +} +} +} + +return nullptr; +} + +/*! @copydoc try_cast */ +template<typename Type> +[[nodiscard]] Type *try_cast() { +if(const auto &info = type_id<Type>(); node && *node->info == info) { +return any_cast<Type>(&storage); +} else if(node) { +for(auto *it = node->base; it; it = it->next) { +if(Type *base = it->cast(as_ref()).template try_cast<Type>(); base) { +return base; +} +} +} + +return nullptr; +} + +/** + * @brief Tries to cast an instance to a given type. + * + * The type of the instance must be such that the cast is possible. + * + * @warning + * Attempting to perform an invalid cast results is undefined behavior. + * + * @tparam Type Type to which to cast the instance. + * @return A reference to the contained instance. + */ +template<typename Type> +[[nodiscard]] Type cast() const { +auto *const instance = try_cast<std::remove_reference_t<Type>>(); +ENTT_ASSERT(instance, "Invalid instance"); +return static_cast<Type>(*instance); +} + +/*! @copydoc cast */ +template<typename Type> +[[nodiscard]] Type cast() { +// forces const on non-reference types to make them work also with wrappers for const references +auto *const instance = try_cast<std::remove_reference_t<const Type>>(); +ENTT_ASSERT(instance, "Invalid instance"); +return static_cast<Type>(*instance); +} + +/** + * @brief Converts an object in such a way that a given cast becomes viable. + * @param type Meta type to which the cast is requested. + * @return A valid meta any object if there exists a viable conversion, an + * invalid one otherwise. + */ +[[nodiscard]] meta_any allow_cast(const meta_type &type) const; + +/** + * @brief Converts an object in such a way that a given cast becomes viable. + * @param type Meta type to which the cast is requested. + * @return True if there exists a viable conversion, false otherwise. + */ +[[nodiscard]] bool allow_cast(const meta_type &type) { +if(auto other = std::as_const(*this).allow_cast(type); other) { +if(other.storage.owner()) { +std::swap(*this, other); +} + +return true; +} + +return false; +} + +/** + * @brief Converts an object in such a way that a given cast becomes viable. + * @tparam Type Type to which the cast is requested. + * @return A valid meta any object if there exists a viable conversion, an + * invalid one otherwise. + */ +template<typename Type> +[[nodiscard]] meta_any allow_cast() const { +const auto other = allow_cast(internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve()); + +if constexpr(std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>) { +return other.storage.owner() ? other : meta_any{}; +} else { +return other; +} +} + +/** + * @brief Converts an object in such a way that a given cast becomes viable. + * @tparam Type Type to which the cast is requested. + * @return True if there exists a viable conversion, false otherwise. + */ +template<typename Type> +bool allow_cast() { +if(auto other = std::as_const(*this).allow_cast(internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve()); other) { +if(other.storage.owner()) { +std::swap(*this, other); +return true; +} + +return (static_cast<constness_as_t<any, std::remove_reference_t<const Type>> &>(storage).data() != nullptr); +} + +return false; +} + +/*! @copydoc any::emplace */ +template<typename Type, typename... Args> +void emplace(Args &&...args) { +release(); +vtable = &basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>; +storage.emplace<Type>(std::forward<Args>(args)...); +node = internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve(); +} + +/*! @copydoc any::assign */ +bool assign(const meta_any &other); + +/*! @copydoc any::assign */ +bool assign(meta_any &&other); + +/*! @copydoc any::reset */ +void reset() { +release(); +vtable = &basic_vtable<void>; +storage.reset(); +node = nullptr; +} + +/** + * @brief Returns a sequence container proxy. + * @return A sequence container proxy for the underlying object. + */ +[[nodiscard]] meta_sequence_container as_sequence_container() ENTT_NOEXCEPT { +any detached = storage.as_ref(); +meta_sequence_container proxy; +vtable(operation::seq, detached, &proxy); +return proxy; +} + +/*! @copydoc as_sequence_container */ +[[nodiscard]] meta_sequence_container as_sequence_container() const ENTT_NOEXCEPT { +any detached = storage.as_ref(); +meta_sequence_container proxy; +vtable(operation::seq, detached, &proxy); +return proxy; +} + +/** + * @brief Returns an associative container proxy. + * @return An associative container proxy for the underlying object. + */ +[[nodiscard]] meta_associative_container as_associative_container() ENTT_NOEXCEPT { +any detached = storage.as_ref(); +meta_associative_container proxy; +vtable(operation::assoc, detached, &proxy); +return proxy; +} + +/*! @copydoc as_associative_container */ +[[nodiscard]] meta_associative_container as_associative_container() const ENTT_NOEXCEPT { +any detached = storage.as_ref(); +meta_associative_container proxy; +vtable(operation::assoc, detached, &proxy); +return proxy; +} + +/** + * @brief Indirection operator for dereferencing opaque objects. + * @return A wrapper that shares a reference to an unmanaged object if the + * wrapped element is dereferenceable, an invalid meta any otherwise. + */ +[[nodiscard]] meta_any operator*() const ENTT_NOEXCEPT { +meta_any ret{}; +vtable(operation::deref, storage, &ret); +return ret; +} + +/** + * @brief Returns false if a wrapper is invalid, true otherwise. + * @return False if the wrapper is invalid, true otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return !(node == nullptr); +} + +/*! @copydoc any::operator== */ +[[nodiscard]] bool operator==(const meta_any &other) const { +return (!node && !other.node) || (node && other.node && *node->info == *other.node->info && storage == other.storage); +} + +/*! @copydoc any::as_ref */ +[[nodiscard]] meta_any as_ref() ENTT_NOEXCEPT { +return meta_any{*this, storage.as_ref()}; +} + +/*! @copydoc any::as_ref */ +[[nodiscard]] meta_any as_ref() const ENTT_NOEXCEPT { +return meta_any{*this, storage.as_ref()}; +} + +/*! @copydoc any::owner */ +[[nodiscard]] bool owner() const ENTT_NOEXCEPT { +return storage.owner(); +} + +private: +any storage; +internal::meta_type_node *node; +vtable_type *vtable; +}; + +/** + * @brief Checks if two wrappers differ in their content. + * @param lhs A wrapper, either empty or not. + * @param rhs A wrapper, either empty or not. + * @return True if the two wrappers differ in their content, false otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_any &lhs, const meta_any &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template<typename Type, typename... Args> +meta_any make_meta(Args &&...args) { +return meta_any{std::in_place_type<Type>, std::forward<Args>(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template<typename Type> +meta_any forward_as_meta(Type &&value) { +return meta_any{std::in_place_type<std::conditional_t<std::is_rvalue_reference_v<Type>, std::decay_t<Type>, Type>>, std::forward<Type>(value)}; +} + +/** + * @brief Opaque pointers to instances of any type. + * + * A handle doesn't perform copies and isn't responsible for the contained + * object. It doesn't prolong the lifetime of the pointed instance.<br/> + * Handles are used to generate references to actual objects when needed. + */ +struct meta_handle { +/*! @brief Default constructor. */ +meta_handle() = default; + +/*! @brief Default copy constructor, deleted on purpose. */ +meta_handle(const meta_handle &) = delete; + +/*! @brief Default move constructor. */ +meta_handle(meta_handle &&) = default; + +/** + * @brief Default copy assignment operator, deleted on purpose. + * @return This meta handle. + */ +meta_handle &operator=(const meta_handle &) = delete; + +/** + * @brief Default move assignment operator. + * @return This meta handle. + */ +meta_handle &operator=(meta_handle &&) = default; + +/** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param value An instance of an object to use to initialize the handle. + */ +template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_handle>>> +meta_handle(Type &value) ENTT_NOEXCEPT +: meta_handle{} { +if constexpr(std::is_same_v<std::decay_t<Type>, meta_any>) { +any = value.as_ref(); +} else { +any.emplace<Type &>(value); +} +} + +/** + * @brief Returns false if a handle is invalid, true otherwise. + * @return False if the handle is invalid, true otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(any); +} + +/** + * @brief Access operator for accessing the contained opaque object. + * @return A wrapper that shares a reference to an unmanaged object. + */ +[[nodiscard]] meta_any *operator->() { +return &any; +} + +/*! @copydoc operator-> */ +[[nodiscard]] const meta_any *operator->() const { +return &any; +} + +private: +meta_any any; +}; + +/*! @brief Opaque wrapper for properties of any type. */ +struct meta_prop { +/*! @brief Node type. */ +using node_type = internal::meta_prop_node; + +/** + * @brief Constructs an instance from a given node. + * @param curr The underlying node with which to construct the instance. + */ +meta_prop(const node_type *curr = nullptr) ENTT_NOEXCEPT +: node{curr} {} + +/** + * @brief Returns the stored key as a const reference. + * @return A wrapper containing the key stored with the property. + */ +[[nodiscard]] meta_any key() const { +return node->id.as_ref(); +} + +/** + * @brief Returns the stored value by copy. + * @return A wrapper containing the value stored with the property. + */ +[[nodiscard]] meta_any value() const { +return node->value; +} + +/** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return !(node == nullptr); +} + +private: +const node_type *node; +}; + +/*! @brief Opaque wrapper for data members. */ +struct meta_data { +/*! @brief Node type. */ +using node_type = internal::meta_data_node; +/*! @brief Unsigned integer type. */ +using size_type = typename node_type::size_type; + +/*! @copydoc meta_prop::meta_prop */ +meta_data(const node_type *curr = nullptr) ENTT_NOEXCEPT +: node{curr} {} + +/*! @copydoc meta_type::id */ +[[nodiscard]] id_type id() const ENTT_NOEXCEPT { +return node->id; +} + +/** + * @brief Returns the number of setters available. + * @return The number of setters available. + */ +[[nodiscard]] size_type arity() const ENTT_NOEXCEPT { +return node->arity; +} + +/** + * @brief Indicates whether a data member is constant or not. + * @return True if the data member is constant, false otherwise. + */ +[[nodiscard]] bool is_const() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_const); +} + +/** + * @brief Indicates whether a data member is static or not. + * @return True if the data member is static, false otherwise. + */ +[[nodiscard]] bool is_static() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_static); +} + +/*! @copydoc meta_any::type */ +[[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; + +/** + * @brief Sets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the data + * member.<br/> + * The type of the value is such that a cast or conversion to the type of + * the variable is possible. Otherwise, invoking the setter does nothing. + * + * @tparam Type Type of value to assign. + * @param instance An opaque instance of the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ +template<typename Type> +bool set(meta_handle instance, Type &&value) const { +return node->set && node->set(std::move(instance), std::forward<Type>(value)); +} + +/** + * @brief Gets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the data + * member. + * + * @param instance An opaque instance of the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ +[[nodiscard]] meta_any get(meta_handle instance) const { +return node->get(std::move(instance)); +} + +/** + * @brief Returns the type accepted by the i-th setter. + * @param index Index of the setter of which to return the accepted type. + * @return The type accepted by the i-th setter. + */ +[[nodiscard]] inline meta_type arg(const size_type index) const ENTT_NOEXCEPT; + +/** + * @brief Returns a range to visit registered meta properties. + * @return An iterable range to visit registered meta properties. + */ +[[nodiscard]] meta_range<meta_prop> prop() const ENTT_NOEXCEPT { +return node->prop; +} + +/** + * @brief Lookup function for registered meta properties. + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ +[[nodiscard]] meta_prop prop(meta_any key) const { +for(auto curr: prop()) { +if(curr.key() == key) { +return curr; +} +} + +return nullptr; +} + +/** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return !(node == nullptr); +} + +private: +const node_type *node; +}; + +/*! @brief Opaque wrapper for member functions. */ +struct meta_func { +/*! @brief Node type. */ +using node_type = internal::meta_func_node; +/*! @brief Unsigned integer type. */ +using size_type = typename node_type::size_type; + +/*! @copydoc meta_prop::meta_prop */ +meta_func(const node_type *curr = nullptr) ENTT_NOEXCEPT +: node{curr} {} + +/*! @copydoc meta_type::id */ +[[nodiscard]] id_type id() const ENTT_NOEXCEPT { +return node->id; +} + +/** + * @brief Returns the number of arguments accepted by a member function. + * @return The number of arguments accepted by the member function. + */ +[[nodiscard]] size_type arity() const ENTT_NOEXCEPT { +return node->arity; +} + +/** + * @brief Indicates whether a member function is constant or not. + * @return True if the member function is constant, false otherwise. + */ +[[nodiscard]] bool is_const() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_const); +} + +/** + * @brief Indicates whether a member function is static or not. + * @return True if the member function is static, false otherwise. + */ +[[nodiscard]] bool is_static() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_static); +} + +/** + * @brief Returns the return type of a member function. + * @return The return type of the member function. + */ +[[nodiscard]] inline meta_type ret() const ENTT_NOEXCEPT; + +/** + * @brief Returns the type of the i-th argument of a member function. + * @param index Index of the argument of which to return the type. + * @return The type of the i-th argument of a member function. + */ +[[nodiscard]] inline meta_type arg(const size_type index) const ENTT_NOEXCEPT; + +/** + * @brief Invokes the underlying function, if possible. + * + * To invoke a member function, the parameters must be such that a cast or + * conversion to the required types is possible. Otherwise, an empty and + * thus invalid wrapper is returned.<br/> + * It must be possible to cast the instance to the parent type of the member + * function. + * + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ +meta_any invoke(meta_handle instance, meta_any *const args, const size_type sz) const { +return sz == arity() ? node->invoke(std::move(instance), args) : meta_any{}; +} + +/** + * @copybrief invoke + * + * @sa invoke + * + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the new instance, if any. + */ +template<typename... Args> +meta_any invoke(meta_handle instance, Args &&...args) const { +meta_any arguments[sizeof...(Args) + 1u]{std::forward<Args>(args)...}; +return invoke(std::move(instance), arguments, sizeof...(Args)); +} + +/*! @copydoc meta_data::prop */ +[[nodiscard]] meta_range<meta_prop> prop() const ENTT_NOEXCEPT { +return node->prop; +} + +/** + * @brief Lookup function for registered meta properties. + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ +[[nodiscard]] meta_prop prop(meta_any key) const { +for(auto curr: prop()) { +if(curr.key() == key) { +return curr; +} +} + +return nullptr; +} + +/** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return !(node == nullptr); +} + +private: +const node_type *node; +}; + +/*! @brief Opaque wrapper for types. */ +class meta_type { +template<auto Member, typename Pred> +[[nodiscard]] std::decay_t<decltype(std::declval<internal::meta_type_node>().*Member)> lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, Pred pred) const { +std::decay_t<decltype(node->*Member)> candidate{}; +size_type extent{sz + 1u}; +bool ambiguous{}; + +for(auto *curr = (node->*Member); curr; curr = curr->next) { +if(pred(curr) && curr->arity == sz) { +size_type direct{}; +size_type ext{}; + +for(size_type next{}; next < sz && next == (direct + ext) && args[next]; ++next) { +const auto type = args[next].type(); +const auto other = curr->arg(next); + +if(const auto &info = other.info(); info == type.info()) { +++direct; +} else { +ext += internal::find_by<&node_type::base>(info, type.node) +|| internal::find_by<&node_type::conv>(info, type.node) +|| (type.node->conversion_helper && other.node->conversion_helper); +} +} + +if((direct + ext) == sz) { +if(ext < extent) { +candidate = curr; +extent = ext; +ambiguous = false; +} else if(ext == extent) { +ambiguous = true; +} +} +} +} + +return (candidate && !ambiguous) ? candidate : decltype(candidate){}; +} + +public: +/*! @brief Node type. */ +using node_type = internal::meta_type_node; +/*! @brief Node type. */ +using base_node_type = internal::meta_base_node; +/*! @brief Unsigned integer type. */ +using size_type = typename node_type::size_type; + +/*! @copydoc meta_prop::meta_prop */ +meta_type(const node_type *curr = nullptr) ENTT_NOEXCEPT +: node{curr} {} + +/** + * @brief Constructs an instance from a given base node. + * @param curr The base node with which to construct the instance. + */ +meta_type(const base_node_type *curr) ENTT_NOEXCEPT +: node{curr ? curr->type : nullptr} {} + +/** + * @brief Returns the type info object of the underlying type. + * @return The type info object of the underlying type. + */ +[[nodiscard]] const type_info &info() const ENTT_NOEXCEPT { +return *node->info; +} + +/** + * @brief Returns the identifier assigned to a type. + * @return The identifier assigned to the type. + */ +[[nodiscard]] id_type id() const ENTT_NOEXCEPT { +return node->id; +} + +/** + * @brief Returns the size of the underlying type if known. + * @return The size of the underlying type if known, 0 otherwise. + */ +[[nodiscard]] size_type size_of() const ENTT_NOEXCEPT { +return node->size_of; +} + +/** + * @brief Checks whether a type refers to an arithmetic type or not. + * @return True if the underlying type is an arithmetic type, false + * otherwise. + */ +[[nodiscard]] bool is_arithmetic() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_arithmetic); +} + +/** + * @brief Checks whether a type refers to an array type or not. + * @return True if the underlying type is an array type, false otherwise. + */ +[[nodiscard]] bool is_array() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_array); +} + +/** + * @brief Checks whether a type refers to an enum or not. + * @return True if the underlying type is an enum, false otherwise. + */ +[[nodiscard]] bool is_enum() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_enum); +} + +/** + * @brief Checks whether a type refers to a class or not. + * @return True if the underlying type is a class, false otherwise. + */ +[[nodiscard]] bool is_class() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_class); +} + +/** + * @brief Checks whether a type refers to a pointer or not. + * @return True if the underlying type is a pointer, false otherwise. + */ +[[nodiscard]] bool is_pointer() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_pointer); +} + +/** + * @brief Checks whether a type is a pointer-like type or not. + * @return True if the underlying type is a pointer-like one, false + * otherwise. + */ +[[nodiscard]] bool is_pointer_like() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_meta_pointer_like); +} + +/** + * @brief Checks whether a type refers to a sequence container or not. + * @return True if the type is a sequence container, false otherwise. + */ +[[nodiscard]] bool is_sequence_container() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_meta_sequence_container); +} + +/** + * @brief Checks whether a type refers to an associative container or not. + * @return True if the type is an associative container, false otherwise. + */ +[[nodiscard]] bool is_associative_container() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_meta_associative_container); +} + +/** + * @brief Checks whether a type refers to a recognized class template + * specialization or not. + * @return True if the type is a recognized class template specialization, + * false otherwise. + */ +[[nodiscard]] bool is_template_specialization() const ENTT_NOEXCEPT { +return (node->templ != nullptr); +} + +/** + * @brief Returns the number of template arguments. + * @return The number of template arguments. + */ +[[nodiscard]] size_type template_arity() const ENTT_NOEXCEPT { +return node->templ ? node->templ->arity : size_type{}; +} + +/** + * @brief Returns a tag for the class template of the underlying type. + * + * @sa meta_class_template_tag + * + * @return The tag for the class template of the underlying type. + */ +[[nodiscard]] inline meta_type template_type() const ENTT_NOEXCEPT { +return node->templ ? node->templ->type : meta_type{}; +} + +/** + * @brief Returns the type of the i-th template argument of a type. + * @param index Index of the template argument of which to return the type. + * @return The type of the i-th template argument of a type. + */ +[[nodiscard]] inline meta_type template_arg(const size_type index) const ENTT_NOEXCEPT { +return index < template_arity() ? node->templ->arg(index) : meta_type{}; +} + +/** + * @brief Returns a range to visit registered top-level base meta types. + * @return An iterable range to visit registered top-level base meta types. + */ +[[nodiscard]] meta_range<meta_type, internal::meta_base_node> base() const ENTT_NOEXCEPT { +return node->base; +} + +/** + * @brief Lookup function for registered base meta types. + * @param id Unique identifier. + * @return The registered base meta type for the given identifier, if any. + */ +[[nodiscard]] meta_type base(const id_type id) const { +return internal::find_by<&node_type::base>(id, node); +} + +/** + * @brief Returns a range to visit registered top-level meta data. + * @return An iterable range to visit registered top-level meta data. + */ +[[nodiscard]] meta_range<meta_data> data() const ENTT_NOEXCEPT { +return node->data; +} + +/** + * @brief Lookup function for registered meta data. + * + * Registered meta data of base classes will also be visited. + * + * @param id Unique identifier. + * @return The registered meta data for the given identifier, if any. + */ +[[nodiscard]] meta_data data(const id_type id) const { +return internal::find_by<&node_type::data>(id, node); +} + +/** + * @brief Returns a range to visit registered top-level functions. + * @return An iterable range to visit registered top-level functions. + */ +[[nodiscard]] meta_range<meta_func> func() const ENTT_NOEXCEPT { +return node->func; +} + +/** + * @brief Lookup function for registered meta functions. + * + * Registered meta functions of base classes will also be visited.<br/> + * In case of overloaded functions, the first one with the required + * identifier will be returned. + * + * @param id Unique identifier. + * @return The registered meta function for the given identifier, if any. + */ +[[nodiscard]] meta_func func(const id_type id) const { +return internal::find_by<&node_type::func>(id, node); +} + +/** + * @brief Creates an instance of the underlying type, if possible. + * + * Parameters are such that a cast or conversion to the required types is + * possible. Otherwise, an empty and thus invalid wrapper is returned.<br/> + * If suitable, the implicitly generated default constructor is used. + * + * @param args Parameters to use to construct the instance. + * @param sz Number of parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ +[[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const { +const auto *candidate = lookup<&node_type::ctor>(args, sz, [](const auto *) { return true; }); +return candidate ? candidate->invoke(args) : ((!sz && node->default_constructor) ? node->default_constructor() : meta_any{}); +} + +/** + * @copybrief construct + * + * @sa construct + * + * @tparam Args Types of arguments to use to construct the instance. + * @param args Parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ +template<typename... Args> +[[nodiscard]] meta_any construct(Args &&...args) const { +meta_any arguments[sizeof...(Args) + 1u]{std::forward<Args>(args)...}; +return construct(arguments, sizeof...(Args)); +} + +/** + * @brief Invokes a function given an identifier, if possible. + * + * It must be possible to cast the instance to the parent type of the member + * function. + * + * @sa meta_func::invoke + * + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ +meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const { +const auto *candidate = lookup<&node_type::func>(args, sz, [id](const auto *curr) { return curr->id == id; }); + +for(auto it = base().begin(), last = base().end(); it != last && !candidate; ++it) { +candidate = it->lookup<&node_type::func>(args, sz, [id](const auto *curr) { return curr->id == id; }); +} + +return candidate ? candidate->invoke(std::move(instance), args) : meta_any{}; +} + +/** + * @copybrief invoke + * + * @sa invoke + * + * @param id Unique identifier. + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the new instance, if any. + */ +template<typename... Args> +meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const { +meta_any arguments[sizeof...(Args) + 1u]{std::forward<Args>(args)...}; +return invoke(id, std::move(instance), arguments, sizeof...(Args)); +} + +/** + * @brief Sets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the data + * member.<br/> + * The type of the value is such that a cast or conversion to the type of + * the variable is possible. Otherwise, invoking the setter does nothing. + * + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ +template<typename Type> +bool set(const id_type id, meta_handle instance, Type &&value) const { +const auto candidate = data(id); +return candidate && candidate.set(std::move(instance), std::forward<Type>(value)); +} + +/** + * @brief Gets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the data + * member. + * + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ +[[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { +const auto candidate = data(id); +return candidate ? candidate.get(std::move(instance)) : meta_any{}; +} + +/** + * @brief Returns a range to visit registered top-level meta properties. + * @return An iterable range to visit registered top-level meta properties. + */ +[[nodiscard]] meta_range<meta_prop> prop() const ENTT_NOEXCEPT { +return node->prop; +} + +/** + * @brief Lookup function for meta properties. + * + * Properties of base classes are also visited. + * + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ +[[nodiscard]] meta_prop prop(meta_any key) const { +return internal::find_by<&internal::meta_type_node::prop>(key, node); +} + +/** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +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_type &other) const ENTT_NOEXCEPT { +return (!node && !other.node) || (node && other.node && *node->info == *other.node->info); +} + +private: +const node_type *node; +}; + +/** + * @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_type &lhs, const meta_type &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +[[nodiscard]] inline meta_type meta_any::type() const ENTT_NOEXCEPT { +return node; +} + +template<typename... Args> +meta_any meta_any::invoke(const id_type id, Args &&...args) const { +return type().invoke(id, *this, std::forward<Args>(args)...); +} + +template<typename... Args> +meta_any meta_any::invoke(const id_type id, Args &&...args) { +return type().invoke(id, *this, std::forward<Args>(args)...); +} + +template<typename Type> +bool meta_any::set(const id_type id, Type &&value) { +return type().set(id, *this, std::forward<Type>(value)); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) const { +return type().get(id, *this); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) { +return type().get(id, *this); +} + +[[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const { +if(const auto &info = type.info(); node && *node->info == info) { +return as_ref(); +} else if(node) { +for(auto *it = node->conv; it; it = it->next) { +if(*it->type->info == info) { +return it->conv(*this); +} +} + +if(node->conversion_helper && (type.is_arithmetic() || type.is_enum())) { +// exploits the fact that arithmetic types and enums are also default constructible +auto other = type.construct(); +ENTT_ASSERT(other.node->conversion_helper, "Conversion helper not found"); +const auto value = node->conversion_helper(nullptr, storage.data()); +other.node->conversion_helper(other.storage.data(), &value); +return other; +} + +for(auto *it = node->base; it; it = it->next) { +const auto as_const = it->cast(as_ref()); + +if(auto other = as_const.allow_cast(type); other) { +return other; +} +} +} + +return {}; +} + +inline bool meta_any::assign(const meta_any &other) { +auto value = other.allow_cast(node); +return value && storage.assign(std::move(value.storage)); +} + +inline bool meta_any::assign(meta_any &&other) { +if(*node->info == *other.node->info) { +return storage.assign(std::move(other.storage)); +} + +return assign(std::as_const(other)); +} + +[[nodiscard]] inline meta_type meta_data::type() const ENTT_NOEXCEPT { +return node->type; +} + +[[nodiscard]] inline meta_type meta_func::ret() const ENTT_NOEXCEPT { +return node->ret; +} + +[[nodiscard]] inline meta_type meta_data::arg(const size_type index) const ENTT_NOEXCEPT { +return index < arity() ? node->arg(index) : meta_type{}; +} + +[[nodiscard]] inline meta_type meta_func::arg(const size_type index) const ENTT_NOEXCEPT { +return index < arity() ? node->arg(index) : meta_type{}; +} + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +class meta_sequence_container::meta_iterator final { +friend class meta_sequence_container; + +using deref_fn_type = meta_any(const any &, const std::ptrdiff_t); + +template<typename It> +static meta_any deref_fn(const any &value, const std::ptrdiff_t pos) { +return meta_any{std::in_place_type<typename std::iterator_traits<It>::reference>, any_cast<const It &>(value)[pos]}; +} + +public: +using difference_type = std::ptrdiff_t; +using value_type = meta_any; +using pointer = input_iterator_pointer<value_type>; +using reference = value_type; +using iterator_category = std::input_iterator_tag; + +meta_iterator() ENTT_NOEXCEPT +: deref{}, +offset{}, +handle{} {} + +template<typename It> +explicit meta_iterator(It iter, const difference_type init) ENTT_NOEXCEPT +: deref{&deref_fn<It>}, +offset{init}, +handle{std::move(iter)} {} + +meta_iterator &operator++() ENTT_NOEXCEPT { +return ++offset, *this; +} + +meta_iterator operator++(int value) ENTT_NOEXCEPT { +meta_iterator orig = *this; +offset += ++value; +return orig; +} + +meta_iterator &operator--() ENTT_NOEXCEPT { +return --offset, *this; +} + +meta_iterator operator--(int value) ENTT_NOEXCEPT { +meta_iterator orig = *this; +offset -= ++value; +return orig; +} + +[[nodiscard]] reference operator*() const { +return deref(handle, offset); +} + +[[nodiscard]] pointer operator->() const { +return operator*(); +} + +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(handle); +} + +[[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { +return offset == other.offset; +} + +[[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { +return !(*this == other); +} + +private: +deref_fn_type *deref; +difference_type offset; +any handle; +}; + +class meta_associative_container::meta_iterator final { +enum class operation : std::uint8_t { +incr, +deref +}; + +using vtable_type = void(const operation, const any &, std::pair<meta_any, meta_any> *); + +template<bool KeyOnly, typename It> +static void basic_vtable(const operation op, const any &value, std::pair<meta_any, meta_any> *other) { +switch(op) { +case operation::incr: +++any_cast<It &>(const_cast<any &>(value)); +break; +case operation::deref: +const auto &it = any_cast<const It &>(value); +if constexpr(KeyOnly) { +other->first.emplace<decltype(*it)>(*it); +} else { +other->first.emplace<decltype((it->first))>(it->first); +other->second.emplace<decltype((it->second))>(it->second); +} +break; +} +} + +public: +using difference_type = std::ptrdiff_t; +using value_type = std::pair<meta_any, meta_any>; +using pointer = input_iterator_pointer<value_type>; +using reference = value_type; +using iterator_category = std::input_iterator_tag; + +meta_iterator() ENTT_NOEXCEPT +: vtable{}, +handle{} {} + +template<bool KeyOnly, typename It> +meta_iterator(std::integral_constant<bool, KeyOnly>, It iter) ENTT_NOEXCEPT +: vtable{&basic_vtable<KeyOnly, It>}, +handle{std::move(iter)} {} + +meta_iterator &operator++() ENTT_NOEXCEPT { +vtable(operation::incr, handle, nullptr); +return *this; +} + +meta_iterator operator++(int) ENTT_NOEXCEPT { +meta_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] reference operator*() const { +reference other; +vtable(operation::deref, handle, &other); +return other; +} + +[[nodiscard]] pointer operator->() const { +return operator*(); +} + +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(handle); +} + +[[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { +return handle == other.handle; +} + +[[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { +return !(*this == other); +} + +private: +vtable_type *vtable; +any handle; +}; + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Returns the meta value type of a container. + * @return The meta value type of the container. + */ +[[nodiscard]] inline meta_type meta_sequence_container::value_type() const ENTT_NOEXCEPT { +return value_type_node; +} + +/** + * @brief Returns the size of a container. + * @return The size of the container. + */ +[[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const ENTT_NOEXCEPT { +return size_fn(storage); +} + +/** + * @brief Resizes a container to contain a given number of elements. + * @param sz The new size of the container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::resize(const size_type sz) { +return resize_fn(storage, sz); +} + +/** + * @brief Clears the content of a container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::clear() { +return resize_fn(storage, 0u); +} + +/** + * @brief Returns an iterator to the first element of a container. + * @return An iterator to the first element of the container. + */ +[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() { +return iter_fn(storage, false); +} + +/** + * @brief Returns an iterator that is past the last element of a container. + * @return An iterator that is past the last element of the container. + */ +[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() { +return iter_fn(storage, true); +} + +/** + * @brief Inserts an element at a specified location of a container. + * @param it Iterator before which the element will be inserted. + * @param value Element value to insert. + * @return A possibly invalid iterator to the inserted element. + */ +inline meta_sequence_container::iterator meta_sequence_container::insert(iterator it, meta_any value) { +return insert_fn(storage, it.offset, value); +} + +/** + * @brief Removes a given element from a container. + * @param it Iterator to the element to remove. + * @return A possibly invalid iterator following the last removed element. + */ +inline meta_sequence_container::iterator meta_sequence_container::erase(iterator it) { +return erase_fn(storage, it.offset); +} + +/** + * @brief Returns a reference to the element at a given location of a container + * (no bounds checking is performed). + * @param pos The position of the element to return. + * @return A reference to the requested element properly wrapped. + */ +[[nodiscard]] inline meta_any meta_sequence_container::operator[](const size_type pos) { +auto it = begin(); +it.operator++(static_cast<int>(pos) - 1); +return *it; +} + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_sequence_container::operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(storage); +} + +/** + * @brief Returns true if a container is also key-only, false otherwise. + * @return True if the associative container is also key-only, false otherwise. + */ +[[nodiscard]] inline bool meta_associative_container::key_only() const ENTT_NOEXCEPT { +return key_only_container; +} + +/** + * @brief Returns the meta key type of a container. + * @return The meta key type of the a container. + */ +[[nodiscard]] inline meta_type meta_associative_container::key_type() const ENTT_NOEXCEPT { +return key_type_node; +} + +/** + * @brief Returns the meta mapped type of a container. + * @return The meta mapped type of the a container. + */ +[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const ENTT_NOEXCEPT { +return mapped_type_node; +} + +/*! @copydoc meta_sequence_container::value_type */ +[[nodiscard]] inline meta_type meta_associative_container::value_type() const ENTT_NOEXCEPT { +return value_type_node; +} + +/*! @copydoc meta_sequence_container::size */ +[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const ENTT_NOEXCEPT { +return size_fn(storage); +} + +/*! @copydoc meta_sequence_container::clear */ +inline bool meta_associative_container::clear() { +return clear_fn(storage); +} + +/*! @copydoc meta_sequence_container::begin */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { +return iter_fn(storage, false); +} + +/*! @copydoc meta_sequence_container::end */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { +return iter_fn(storage, true); +} + +/** + * @brief Inserts an element (a key/value pair) into a container. + * @param key The key of the element to insert. + * @param value The value of the element to insert. + * @return A bool denoting whether the insertion took place. + */ +inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) { +return insert_fn(storage, key, value); +} + +/** + * @brief Removes the specified element from a container. + * @param key The key of the element to remove. + * @return A bool denoting whether the removal took place. + */ +inline bool meta_associative_container::erase(meta_any key) { +return erase_fn(storage, key); +} + +/** + * @brief Returns an iterator to the element with a given key, if any. + * @param key The key of the element to search. + * @return An iterator to the element with the given key, if any. + */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) { +return find_fn(storage, key); +} + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_associative_container::operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(storage); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct is_dynamic_sequence_container: std::false_type {}; + +template<typename Type> +struct is_dynamic_sequence_container<Type, std::void_t<decltype(&Type::reserve)>>: std::true_type {}; + +template<typename, typename = void> +struct is_key_only_meta_associative_container: std::true_type {}; + +template<typename Type> +struct is_key_only_meta_associative_container<Type, std::void_t<typename Type::mapped_type>>: std::false_type {}; + +template<typename Type> +struct basic_meta_sequence_container_traits { +using iterator = meta_sequence_container::iterator; +using size_type = std::size_t; + +[[nodiscard]] static size_type size(const any &container) ENTT_NOEXCEPT { +return any_cast<const Type &>(container).size(); +} + +[[nodiscard]] static bool resize([[maybe_unused]] any &container, [[maybe_unused]] size_type sz) { +if constexpr(is_dynamic_sequence_container<Type>::value) { +if(auto *const cont = any_cast<Type>(&container); cont) { +cont->resize(sz); +return true; +} +} + +return false; +} + +[[nodiscard]] static iterator iter(any &container, const bool as_end) { +using std::begin; + +if(auto *const cont = any_cast<Type>(&container); cont) { +return iterator{begin(*cont), static_cast<typename iterator::difference_type>(as_end * cont->size())}; +} + +const Type &as_const = any_cast<const Type &>(container); +return iterator{begin(as_const), static_cast<typename iterator::difference_type>(as_end * as_const.size())}; +} + +[[nodiscard]] static iterator insert([[maybe_unused]] any &container, [[maybe_unused]] const std::ptrdiff_t offset, [[maybe_unused]] meta_any &value) { +if constexpr(is_dynamic_sequence_container<Type>::value) { +if(auto *const cont = any_cast<Type>(&container); cont) { +// this abomination is necessary because only on macos value_type and const_reference are different types for std::vector<bool> +if(value.allow_cast<typename Type::const_reference>() || value.allow_cast<typename Type::value_type>()) { +using std::begin; +const auto *element = value.try_cast<std::remove_reference_t<typename Type::const_reference>>(); +const auto curr = cont->insert(begin(*cont) + offset, element ? *element : value.cast<typename Type::value_type>()); +return iterator{curr, curr - begin(*cont)}; +} +} +} + +return {}; +} + +[[nodiscard]] static iterator erase([[maybe_unused]] any &container, [[maybe_unused]] const std::ptrdiff_t offset) { +if constexpr(is_dynamic_sequence_container<Type>::value) { +if(auto *const cont = any_cast<Type>(&container); cont) { +using std::begin; +const auto curr = cont->erase(begin(*cont) + offset); +return iterator{curr, curr - begin(*cont)}; +} +} + +return {}; +} +}; + +template<typename Type> +struct basic_meta_associative_container_traits { +using iterator = meta_associative_container::iterator; +using size_type = std::size_t; + +static constexpr auto key_only = is_key_only_meta_associative_container<Type>::value; + +[[nodiscard]] static size_type size(const any &container) ENTT_NOEXCEPT { +return any_cast<const Type &>(container).size(); +} + +[[nodiscard]] static bool clear(any &container) { +if(auto *const cont = any_cast<Type>(&container); cont) { +cont->clear(); +return true; +} + +return false; +} + +[[nodiscard]] static iterator iter(any &container, const bool as_end) { +using std::begin; +using std::end; + +if(auto *const cont = any_cast<Type>(&container); cont) { +return iterator{std::integral_constant<bool, key_only>{}, as_end ? end(*cont) : begin(*cont)}; +} + +const auto &as_const = any_cast<const Type &>(container); +return iterator{std::integral_constant<bool, key_only>{}, as_end ? end(as_const) : begin(as_const)}; +} + +[[nodiscard]] static bool insert(any &container, meta_any &key, [[maybe_unused]] meta_any &value) { +auto *const cont = any_cast<Type>(&container); + +if constexpr(is_key_only_meta_associative_container<Type>::value) { +return cont && key.allow_cast<const typename Type::key_type &>() +&& cont->insert(key.cast<const typename Type::key_type &>()).second; +} else { +return cont && key.allow_cast<const typename Type::key_type &>() && value.allow_cast<const typename Type::mapped_type &>() +&& cont->emplace(key.cast<const typename Type::key_type &>(), value.cast<const typename Type::mapped_type &>()).second; +} +} + +[[nodiscard]] static bool erase(any &container, meta_any &key) { +auto *const cont = any_cast<Type>(&container); +return cont && key.allow_cast<const typename Type::key_type &>() +&& (cont->erase(key.cast<const typename Type::key_type &>()) != cont->size()); +} + +[[nodiscard]] static iterator find(any &container, meta_any &key) { +if(key.allow_cast<const typename Type::key_type &>()) { +if(auto *const cont = any_cast<Type>(&container); cont) { +return iterator{std::integral_constant<bool, key_only>{}, cont->find(key.cast<const typename Type::key_type &>())}; +} + +return iterator{std::integral_constant<bool, key_only>{}, any_cast<const Type &>(container).find(key.cast<const typename Type::key_type &>())}; +} + +return {}; +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Meta sequence container traits for `std::vector`s of any type. + * @tparam Type The type of elements. + * @tparam Args Other arguments. + */ +template<typename Type, typename... Args> +struct meta_sequence_container_traits<std::vector<Type, Args...>> +: internal::basic_meta_sequence_container_traits<std::vector<Type, Args...>> {}; + +/** + * @brief Meta sequence container traits for `std::array`s of any type. + * @tparam Type The type of elements. + * @tparam N The number of elements. + */ +template<typename Type, auto N> +struct meta_sequence_container_traits<std::array<Type, N>> +: internal::basic_meta_sequence_container_traits<std::array<Type, N>> {}; + +/** + * @brief Meta associative container traits for `std::map`s of any type. + * @tparam Key The key type of elements. + * @tparam Value The value type of elements. + * @tparam Args Other arguments. + */ +template<typename Key, typename Value, typename... Args> +struct meta_associative_container_traits<std::map<Key, Value, Args...>> +: internal::basic_meta_associative_container_traits<std::map<Key, Value, Args...>> {}; + +/** + * @brief Meta associative container traits for `std::unordered_map`s of any + * type. + * @tparam Key The key type of elements. + * @tparam Value The value type of elements. + * @tparam Args Other arguments. + */ +template<typename Key, typename Value, typename... Args> +struct meta_associative_container_traits<std::unordered_map<Key, Value, Args...>> +: internal::basic_meta_associative_container_traits<std::unordered_map<Key, Value, Args...>> {}; + +/** + * @brief Meta associative container traits for `std::set`s of any type. + * @tparam Key The type of elements. + * @tparam Args Other arguments. + */ +template<typename Key, typename... Args> +struct meta_associative_container_traits<std::set<Key, Args...>> +: internal::basic_meta_associative_container_traits<std::set<Key, Args...>> {}; + +/** + * @brief Meta associative container traits for `std::unordered_set`s of any + * type. + * @tparam Key The type of elements. + * @tparam Args Other arguments. + */ +template<typename Key, typename... Args> +struct meta_associative_container_traits<std::unordered_set<Key, Args...>> +: internal::basic_meta_associative_container_traits<std::unordered_set<Key, Args...>> {}; + +/** + * @brief Meta associative container traits for `dense_map`s of any type. + * @tparam Key The key type of the elements. + * @tparam Type The value type of the elements. + * @tparam Args Other arguments. + */ +template<typename Key, typename Type, typename... Args> +struct meta_associative_container_traits<dense_map<Key, Type, Args...>> +: internal::basic_meta_associative_container_traits<dense_map<Key, Type, Args...>> {}; + +/** + * @brief Meta associative container traits for `dense_set`s of any type. + * @tparam Type The value type of the elements. + * @tparam Args Other arguments. + */ +template<typename Type, typename... Args> +struct meta_associative_container_traits<dense_set<Type, Args...>> +: internal::basic_meta_associative_container_traits<dense_set<Type, Args...>> {}; + +} // namespace entt + +#endif + +// #include "meta/ctx.hpp" +#ifndef ENTT_META_CTX_HPP +#define ENTT_META_CTX_HPP + +// #include "../config/config.h" + +// #include "../core/attribute.h" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +struct meta_type_node; + +struct ENTT_API meta_context { +// we could use the lines below but VS2017 returns with an ICE if combined with ENTT_API despite the code being valid C++ +// inline static meta_type_node *local = nullptr; +// inline static meta_type_node **global = &local; + +[[nodiscard]] static meta_type_node *&local() ENTT_NOEXCEPT { +static meta_type_node *chain = nullptr; +return chain; +} + +[[nodiscard]] static meta_type_node **&global() ENTT_NOEXCEPT { +static meta_type_node **chain = &local(); +return chain; +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @brief Opaque container for a meta context. */ +struct meta_ctx { +/** + * @brief Binds the meta system to a given context. + * @param other A valid context to which to bind. + */ +static void bind(meta_ctx other) ENTT_NOEXCEPT { +internal::meta_context::global() = other.ctx; +} + +private: +internal::meta_type_node **ctx{&internal::meta_context::local()}; +}; + +} // namespace entt + +#endif + +// #include "meta/factory.hpp" +#ifndef ENTT_META_FACTORY_HPP +#define ENTT_META_FACTORY_HPP + +#include <algorithm> +#include <cstddef> +#include <functional> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "policy.hpp" +#ifndef ENTT_META_POLICY_HPP +#define ENTT_META_POLICY_HPP + +#include <type_traits> + +namespace entt { + +/*! @brief Empty class type used to request the _as ref_ policy. */ +struct as_ref_t { +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ +template<typename Type> +static constexpr bool value = std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>; +/** + * Internal details not to be documented. + * @endcond + */ +}; + +/*! @brief Empty class type used to request the _as cref_ policy. */ +struct as_cref_t { +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ +template<typename Type> +static constexpr bool value = std::is_reference_v<Type>; +/** + * Internal details not to be documented. + * @endcond + */ +}; + +/*! @brief Empty class type used to request the _as-is_ policy. */ +struct as_is_t { +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ +template<typename> +static constexpr bool value = true; +/** + * Internal details not to be documented. + * @endcond + */ +}; + +/*! @brief Empty class type used to request the _as void_ policy. */ +struct as_void_t { +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ +template<typename> +static constexpr bool value = true; +/** + * Internal details not to be documented. + * @endcond + */ +}; + +} // namespace entt + +#endif + +// #include "range.hpp" + +// #include "utility.hpp" +#ifndef ENTT_META_UTILITY_HPP +#define ENTT_META_UTILITY_HPP + +#include <cstddef> +#include <functional> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "policy.hpp" + + +namespace entt { + +/*! @brief Primary template isn't defined on purpose. */ +template<typename, typename> +struct meta_function_descriptor; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template<typename Type, typename Ret, typename Class, typename... Args> +struct meta_function_descriptor<Type, Ret (Class::*)(Args...) const> { +/*! @brief Meta function return type. */ +using return_type = Ret; +/*! @brief Meta function arguments. */ +using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<Args...>, type_list<const Class &, Args...>>; + +/*! @brief True if the meta function is const, false otherwise. */ +static constexpr auto is_const = true; +/*! @brief True if the meta function is static, false otherwise. */ +static constexpr auto is_static = !std::is_base_of_v<Class, Type>; +}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template<typename Type, typename Ret, typename Class, typename... Args> +struct meta_function_descriptor<Type, Ret (Class::*)(Args...)> { +/*! @brief Meta function return type. */ +using return_type = Ret; +/*! @brief Meta function arguments. */ +using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<Args...>, type_list<Class &, Args...>>; + +/*! @brief True if the meta function is const, false otherwise. */ +static constexpr auto is_const = false; +/*! @brief True if the meta function is static, false otherwise. */ +static constexpr auto is_static = !std::is_base_of_v<Class, Type>; +}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta data is associated. + * @tparam Class Actual owner of the data member. + * @tparam Ret Data member type. + */ +template<typename Type, typename Ret, typename Class> +struct meta_function_descriptor<Type, Ret Class::*> { +/*! @brief Meta data return type. */ +using return_type = Ret &; +/*! @brief Meta data arguments. */ +using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<>, type_list<Class &>>; + +/*! @brief True if the meta data is const, false otherwise. */ +static constexpr auto is_const = false; +/*! @brief True if the meta data is static, false otherwise. */ +static constexpr auto is_static = !std::is_base_of_v<Class, Type>; +}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam MaybeType First function argument. + * @tparam Args Other function arguments. + */ +template<typename Type, typename Ret, typename MaybeType, typename... Args> +struct meta_function_descriptor<Type, Ret (*)(MaybeType, Args...)> { +/*! @brief Meta function return type. */ +using return_type = Ret; +/*! @brief Meta function arguments. */ +using args_type = 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...>>; + +/*! @brief True if the meta function is const, false otherwise. */ +static constexpr auto is_const = std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> && std::is_const_v<std::remove_reference_t<MaybeType>>; +/*! @brief True if the meta function is static, false otherwise. */ +static constexpr auto is_static = !std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>; +}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + */ +template<typename Type, typename Ret> +struct meta_function_descriptor<Type, Ret (*)()> { +/*! @brief Meta function return type. */ +using return_type = Ret; +/*! @brief Meta function arguments. */ +using args_type = type_list<>; + +/*! @brief True if the meta function is const, false otherwise. */ +static constexpr auto is_const = false; +/*! @brief True if the meta function is static, false otherwise. */ +static constexpr auto is_static = true; +}; + +/** + * @brief Meta function helper. + * + * Converts a function type to be associated with a reflected type into its meta + * function descriptor. + * + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Candidate The actual function to associate with the reflected type. + */ +template<typename Type, typename Candidate> +class meta_function_helper { +template<typename Ret, typename... Args, typename Class> +static constexpr meta_function_descriptor<Type, Ret (Class::*)(Args...) const> get_rid_of_noexcept(Ret (Class::*)(Args...) const); + +template<typename Ret, typename... Args, typename Class> +static constexpr meta_function_descriptor<Type, Ret (Class::*)(Args...)> get_rid_of_noexcept(Ret (Class::*)(Args...)); + +template<typename Ret, typename Class> +static constexpr meta_function_descriptor<Type, Ret Class::*> get_rid_of_noexcept(Ret Class::*); + +template<typename Ret, typename... Args> +static constexpr meta_function_descriptor<Type, Ret (*)(Args...)> get_rid_of_noexcept(Ret (*)(Args...)); + +template<typename Class> +static constexpr meta_function_descriptor<Class, decltype(&Class::operator())> get_rid_of_noexcept(Class); + +public: +/*! @brief The meta function descriptor of the given function. */ +using type = decltype(get_rid_of_noexcept(std::declval<Candidate>())); +}; + +/** + * @brief Helper type. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Candidate The actual function to associate with the reflected type. + */ +template<typename Type, typename Candidate> +using meta_function_helper_t = typename meta_function_helper<Type, Candidate>::type; + +/** + * @brief Wraps a value depending on the given policy. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Type Type of value to wrap. + * @param value Value to wrap. + * @return A meta any containing the returned value, if any. + */ +template<typename Policy = as_is_t, typename Type> +meta_any meta_dispatch([[maybe_unused]] Type &&value) { +if constexpr(std::is_same_v<Policy, as_void_t>) { +return meta_any{std::in_place_type<void>}; +} else if constexpr(std::is_same_v<Policy, as_ref_t>) { +return meta_any{std::in_place_type<Type>, std::forward<Type>(value)}; +} else if constexpr(std::is_same_v<Policy, as_cref_t>) { +static_assert(std::is_lvalue_reference_v<Type>, "Invalid type"); +return meta_any{std::in_place_type<const std::remove_reference_t<Type> &>, std::as_const(value)}; +} else { +static_assert(std::is_same_v<Policy, as_is_t>, "Policy not supported"); +return meta_any{std::forward<Type>(value)}; +} +} + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @return The meta type of the i-th element of the list of arguments. + */ +template<typename Type> +[[nodiscard]] static meta_type meta_arg(const std::size_t index) ENTT_NOEXCEPT { +return internal::meta_arg_node(Type{}, index); +} + +/** + * @brief Sets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to set. + * @param instance An opaque instance of the underlying type, if required. + * @param value Parameter to use to set the variable. + * @return True in case of success, false otherwise. + */ +template<typename Type, auto Data> +[[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { +if constexpr(!std::is_same_v<decltype(Data), Type> && !std::is_same_v<decltype(Data), std::nullptr_t>) { +if constexpr(std::is_member_function_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) { +using descriptor = meta_function_helper_t<Type, decltype(Data)>; +using data_type = type_list_element_t<descriptor::is_static, typename descriptor::args_type>; + +if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) { +std::invoke(Data, *clazz, value.cast<data_type>()); +return true; +} +} else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) { +using data_type = std::remove_reference_t<typename meta_function_helper_t<Type, decltype(Data)>::return_type>; + +if constexpr(!std::is_array_v<data_type> && !std::is_const_v<data_type>) { +if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) { +std::invoke(Data, *clazz) = value.cast<data_type>(); +return true; +} +} +} else { +using data_type = std::remove_reference_t<decltype(*Data)>; + +if constexpr(!std::is_array_v<data_type> && !std::is_const_v<data_type>) { +if(value.allow_cast<data_type>()) { +*Data = value.cast<data_type>(); +return true; +} +} +} +} + +return false; +} + +/** + * @brief Gets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to get. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @return A meta any containing the value of the underlying variable. + */ +template<typename Type, auto Data, typename Policy = as_is_t> +[[nodiscard]] meta_any meta_getter([[maybe_unused]] meta_handle instance) { +if constexpr(std::is_member_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) { +if constexpr(!std::is_array_v<std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>>>) { +if constexpr(std::is_invocable_v<decltype(Data), Type &>) { +if(auto *clazz = instance->try_cast<Type>(); clazz) { +return meta_dispatch<Policy>(std::invoke(Data, *clazz)); +} +} + +if constexpr(std::is_invocable_v<decltype(Data), const Type &>) { +if(auto *fallback = instance->try_cast<const Type>(); fallback) { +return meta_dispatch<Policy>(std::invoke(Data, *fallback)); +} +} +} + +return meta_any{}; +} else if constexpr(std::is_pointer_v<decltype(Data)>) { +if constexpr(std::is_array_v<std::remove_pointer_t<decltype(Data)>>) { +return meta_any{}; +} else { +return meta_dispatch<Policy>(*Data); +} +} else { +return meta_dispatch<Policy>(Data); +} +} + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type, typename Policy, typename Candidate, typename... Args> +[[nodiscard]] meta_any meta_invoke_with_args(Candidate &&candidate, Args &&...args) { +if constexpr(std::is_same_v<std::invoke_result_t<decltype(candidate), Args...>, void>) { +std::invoke(candidate, args...); +return meta_any{std::in_place_type<void>}; +} else { +return meta_dispatch<Policy>(std::invoke(candidate, args...)); +} +} + +template<typename Type, typename Policy, typename Candidate, std::size_t... Index> +[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence<Index...>) { +using descriptor = meta_function_helper_t<Type, std::remove_reference_t<Candidate>>; + +if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, const Type &, type_list_element_t<Index, typename descriptor::args_type>...>) { +if(const auto *const clazz = instance->try_cast<const Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) { +return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...); +} +} else if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, Type &, type_list_element_t<Index, typename descriptor::args_type>...>) { +if(auto *const clazz = instance->try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) { +return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...); +} +} else { +if(((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) { +return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...); +} +} + +return meta_any{}; +} + +template<typename Type, typename... Args, std::size_t... Index> +[[nodiscard]] meta_any meta_construct(meta_any *const args, std::index_sequence<Index...>) { +if(((args + Index)->allow_cast<Args>() && ...)) { +return meta_any{std::in_place_type<Type>, (args + Index)->cast<Args>()...}; +} + +return meta_any{}; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Tries to _invoke_ an object given a list of erased parameters. + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param instance An opaque instance of the underlying type, if required. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template<typename Type, typename Policy = as_is_t, typename Candidate> +[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args) { +return internal::meta_invoke<Type, Policy>(std::move(instance), std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{}); +} + +/** + * @brief Tries to invoke a function given a list of erased parameters. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template<typename Type, auto Candidate, typename Policy = as_is_t> +[[nodiscard]] meta_any meta_invoke(meta_handle instance, meta_any *const args) { +return internal::meta_invoke<Type, Policy>(std::move(instance), Candidate, args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{}); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template<typename Type, typename... Args> +[[nodiscard]] meta_any meta_construct(meta_any *const args) { +return internal::meta_construct<Type, Args...>(args, std::index_sequence_for<Args...>{}); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @param candidate The actual object to _invoke_. + * @return A meta any containing the returned value, if any. + */ +template<typename Type, typename Policy = as_is_t, typename Candidate> +[[nodiscard]] meta_any meta_construct(Candidate &&candidate, meta_any *const args) { +if constexpr(meta_function_helper_t<Type, Candidate>::is_static) { +return internal::meta_invoke<Type, Policy>({}, std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{}); +} else { +return internal::meta_invoke<Type, Policy>(*args, std::forward<Candidate>(candidate), args + 1u, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{}); +} +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template<typename Type, auto Candidate, typename Policy = as_is_t> +[[nodiscard]] meta_any meta_construct(meta_any *const args) { +return meta_construct<Type, Policy>(Candidate, args); +} + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Meta factory to be used for reflection purposes. + * + * The meta factory is an utility class used to reflect types, data members and + * functions of all sorts. This class ensures that the underlying web of types + * is built correctly and performs some checks in debug mode to ensure that + * there are no subtle errors at runtime. + */ +template<typename...> +class meta_factory; + +/** + * @brief Extended meta factory to be used for reflection purposes. + * @tparam Type Reflected type for which the factory was created. + * @tparam Spec Property specialization pack used to disambiguate overloads. + */ +template<typename Type, typename... Spec> +class meta_factory<Type, Spec...>: public meta_factory<Type> { +void link_prop_if_required(internal::meta_prop_node &node) ENTT_NOEXCEPT { +if(meta_range<internal::meta_prop_node *, internal::meta_prop_node> range{*ref}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) { +ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [&node](const auto *curr) { return curr->id == node.id; }) == range.cend(), "Duplicate identifier"); +node.next = *ref; +*ref = &node; +} +} + +template<std::size_t Step = 0, typename... Property, typename... Other> +void unroll(choice_t<2>, std::tuple<Property...> property, Other &&...other) ENTT_NOEXCEPT { +std::apply([this](auto &&...curr) { (this->unroll<Step>(choice<2>, std::forward<Property>(curr)...)); }, property); +unroll<Step + sizeof...(Property)>(choice<2>, std::forward<Other>(other)...); +} + +template<std::size_t Step = 0, typename... Property, typename... Other> +void unroll(choice_t<1>, std::pair<Property...> property, Other &&...other) ENTT_NOEXCEPT { +assign<Step>(std::move(property.first), std::move(property.second)); +unroll<Step + 1>(choice<2>, std::forward<Other>(other)...); +} + +template<std::size_t Step = 0, typename Property, typename... Other> +void unroll(choice_t<0>, Property &&property, Other &&...other) ENTT_NOEXCEPT { +assign<Step>(std::forward<Property>(property)); +unroll<Step + 1>(choice<2>, std::forward<Other>(other)...); +} + +template<std::size_t> +void unroll(choice_t<0>) ENTT_NOEXCEPT {} + +template<std::size_t = 0> +void assign(meta_any key, meta_any value = {}) { +static meta_any property[2u]{}; + +static internal::meta_prop_node node{ +nullptr, +property[0u], +property[1u] +// tricks clang-format +}; + +property[0u] = std::move(key); +property[1u] = std::move(value); + +link_prop_if_required(node); +} + +public: +/** + * @brief Constructs an extended factory from a given node. + * @param target The underlying node to which to assign the properties. + */ +meta_factory(internal::meta_prop_node **target) ENTT_NOEXCEPT +: ref{target} {} + +/** + * @brief Assigns a property to the last meta object created. + * + * Both the key and the value (if any) must be at least copy constructible. + * + * @tparam PropertyOrKey Type of the property or property key. + * @tparam Value Optional type of the property value. + * @param property_or_key Property or property key. + * @param value Optional property value. + * @return A meta factory for the parent type. + */ +template<typename PropertyOrKey, typename... Value> +meta_factory<Type> prop(PropertyOrKey &&property_or_key, Value &&...value) { +if constexpr(sizeof...(Value) == 0) { +unroll(choice<2>, std::forward<PropertyOrKey>(property_or_key)); +} else { +assign(std::forward<PropertyOrKey>(property_or_key), std::forward<Value>(value)...); +} + +return {}; +} + +/** + * @brief Assigns properties to the last meta object created. + * + * Both key and value (if any) must be at least copy constructible. + * + * @tparam Property Types of the properties. + * @param property Properties to assign to the last meta object created. + * @return A meta factory for the parent type. + */ +template<typename... Property> +meta_factory<Type> props(Property... property) { +unroll(choice<2>, std::forward<Property>(property)...); +return {}; +} + +private: +internal::meta_prop_node **ref; +}; + +/** + * @brief Basic meta factory to be used for reflection purposes. + * @tparam Type Reflected type for which the factory was created. + */ +template<typename Type> +class meta_factory<Type> { +void link_base_if_required(internal::meta_base_node &node) ENTT_NOEXCEPT { +if(meta_range<internal::meta_base_node *, internal::meta_base_node> range{owner->base}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) { +node.next = owner->base; +owner->base = &node; +} +} + +void link_conv_if_required(internal::meta_conv_node &node) ENTT_NOEXCEPT { +if(meta_range<internal::meta_conv_node *, internal::meta_conv_node> range{owner->conv}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) { +node.next = owner->conv; +owner->conv = &node; +} +} + +void link_ctor_if_required(internal::meta_ctor_node &node) ENTT_NOEXCEPT { +if(meta_range<internal::meta_ctor_node *, internal::meta_ctor_node> range{owner->ctor}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) { +node.next = owner->ctor; +owner->ctor = &node; +} +} + +void link_data_if_required(const id_type id, internal::meta_data_node &node) ENTT_NOEXCEPT { +meta_range<internal::meta_data_node *, internal::meta_data_node> range{owner->data}; +ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [id, &node](const auto *curr) { return curr != &node && curr->id == id; }) == range.cend(), "Duplicate identifier"); +node.id = id; + +if(std::find(range.cbegin(), range.cend(), &node) == range.cend()) { +node.next = owner->data; +owner->data = &node; +} +} + +void link_func_if_required(const id_type id, internal::meta_func_node &node) ENTT_NOEXCEPT { +node.id = id; + +if(meta_range<internal::meta_func_node *, internal::meta_func_node> range{owner->func}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) { +node.next = owner->func; +owner->func = &node; +} +} + +template<typename Setter, auto Getter, typename Policy, std::size_t... Index> +auto data(const id_type id, std::index_sequence<Index...>) ENTT_NOEXCEPT { +using data_type = std::invoke_result_t<decltype(Getter), Type &>; +using args_type = type_list<typename meta_function_helper_t<Type, decltype(value_list_element_v<Index, Setter>)>::args_type...>; +static_assert(Policy::template value<data_type>, "Invalid return type for the given policy"); + +static internal::meta_data_node node{ +{}, +/* this is never static */ +(std::is_member_object_pointer_v<decltype(value_list_element_v<Index, Setter>)> && ... && std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none, +nullptr, +nullptr, +Setter::size, +internal::meta_node<std::remove_cv_t<std::remove_reference_t<data_type>>>::resolve(), +&meta_arg<type_list<type_list_element_t<type_list_element_t<Index, args_type>::size != 1u, type_list_element_t<Index, args_type>>...>>, +[](meta_handle instance, meta_any value) -> bool { return (meta_setter<Type, value_list_element_v<Index, Setter>>(*instance.operator->(), value.as_ref()) || ...); }, +&meta_getter<Type, Getter, Policy> +// tricks clang-format +}; + +link_data_if_required(id, node); +return meta_factory<Type, Setter, std::integral_constant<decltype(Getter), Getter>>{&node.prop}; +} + +public: +/*! @brief Default constructor. */ +meta_factory() ENTT_NOEXCEPT +: owner{internal::meta_node<Type>::resolve()} {} + +/** + * @brief Makes a meta type _searchable_. + * @param id Optional unique identifier. + * @return An extended meta factory for the given type. + */ +auto type(const id_type id = type_hash<Type>::value()) ENTT_NOEXCEPT { +meta_range<internal::meta_type_node *, internal::meta_type_node> range{*internal::meta_context::global()}; +ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [id, this](const auto *curr) { return curr != owner && curr->id == id; }) == range.cend(), "Duplicate identifier"); +owner->id = id; + +if(std::find(range.cbegin(), range.cend(), owner) == range.cend()) { +owner->next = *internal::meta_context::global(); +*internal::meta_context::global() = owner; +} + +return meta_factory<Type, Type>{&owner->prop}; +} + +/** + * @brief Assigns a meta base to a meta type. + * + * A reflected base class must be a real base class of the reflected type. + * + * @tparam Base Type of the base class to assign to the meta type. + * @return A meta factory for the parent type. + */ +template<typename Base> +auto base() ENTT_NOEXCEPT { +static_assert(!std::is_same_v<Type, Base> && std::is_base_of_v<Base, Type>, "Invalid base type"); + +static internal::meta_base_node node{ +nullptr, +internal::meta_node<Base>::resolve(), +[](meta_any other) ENTT_NOEXCEPT -> meta_any { +if(auto *ptr = other.data(); ptr) { +return forward_as_meta(*static_cast<Base *>(static_cast<Type *>(ptr))); +} + +return forward_as_meta(*static_cast<const Base *>(static_cast<const Type *>(std::as_const(other).data()))); +} +// tricks clang-format +}; + +link_base_if_required(node); +return meta_factory<Type>{}; +} + +/** + * @brief Assigns a meta conversion function to a meta type. + * + * Conversion functions can be either free functions or member + * functions.<br/> + * In case of free functions, they must accept a const reference to an + * instance of the parent type as an argument. In case of member functions, + * they should have no arguments at all. + * + * @tparam Candidate The actual function to use for the conversion. + * @return A meta factory for the parent type. + */ +template<auto Candidate> +auto conv() ENTT_NOEXCEPT { +static internal::meta_conv_node node{ +nullptr, +internal::meta_node<std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>>::resolve(), +[](const meta_any &instance) -> meta_any { +return forward_as_meta(std::invoke(Candidate, *static_cast<const Type *>(instance.data()))); +} +// tricks clang-format +}; + +link_conv_if_required(node); +return meta_factory<Type>{}; +} + +/** + * @brief Assigns a meta conversion function to a meta type. + * + * The given type must be such that an instance of the reflected type can be + * converted to it. + * + * @tparam To Type of the conversion function to assign to the meta type. + * @return A meta factory for the parent type. + */ +template<typename To> +auto conv() ENTT_NOEXCEPT { +static internal::meta_conv_node node{ +nullptr, +internal::meta_node<std::remove_cv_t<std::remove_reference_t<To>>>::resolve(), +[](const meta_any &instance) -> meta_any { return forward_as_meta(static_cast<To>(*static_cast<const Type *>(instance.data()))); } +// tricks clang-format +}; + +link_conv_if_required(node); +return meta_factory<Type>{}; +} + +/** + * @brief Assigns a meta constructor to a meta type. + * + * Both member functions and free function can be assigned to meta types in + * the role of constructors. All that is required is that they return an + * instance of the underlying type.<br/> + * From a client's point of view, nothing changes if a constructor of a meta + * type is a built-in one or not. + * + * @tparam Candidate The actual function to use as a constructor. + * @tparam Policy Optional policy (no policy set by default). + * @return An extended meta factory for the parent type. + */ +template<auto Candidate, typename Policy = as_is_t> +auto ctor() ENTT_NOEXCEPT { +using descriptor = meta_function_helper_t<Type, decltype(Candidate)>; +static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy"); +static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>, Type>, "The function doesn't return an object of the required type"); + +static internal::meta_ctor_node node{ +nullptr, +descriptor::args_type::size, +&meta_arg<typename descriptor::args_type>, +&meta_construct<Type, Candidate, Policy> +// tricks clang-format +}; + +link_ctor_if_required(node); +return meta_factory<Type>{}; +} + +/** + * @brief Assigns a meta constructor to a meta type. + * + * A meta constructor is uniquely identified by the types of its arguments + * and is such that there exists an actual constructor of the underlying + * type that can be invoked with parameters whose types are those given. + * + * @tparam Args Types of arguments to use to construct an instance. + * @return An extended meta factory for the parent type. + */ +template<typename... Args> +auto ctor() ENTT_NOEXCEPT { +using descriptor = meta_function_helper_t<Type, Type (*)(Args...)>; + +static internal::meta_ctor_node node{ +nullptr, +descriptor::args_type::size, +&meta_arg<typename descriptor::args_type>, +&meta_construct<Type, Args...> +// tricks clang-format +}; + +link_ctor_if_required(node); +return meta_factory<Type>{}; +} + +/** + * @brief Assigns a meta destructor to a meta type. + * + * Both free functions and member functions can be assigned to meta types in + * the role of destructors.<br/> + * The signature of a free function should be identical to the following: + * + * @code{.cpp} + * void(Type &); + * @endcode + * + * Member functions should not take arguments instead.<br/> + * The purpose is to give users the ability to free up resources that + * require special treatment before an object is actually destroyed. + * + * @tparam Func The actual function to use as a destructor. + * @return A meta factory for the parent type. + */ +template<auto Func> +auto dtor() ENTT_NOEXCEPT { +static_assert(std::is_invocable_v<decltype(Func), Type &>, "The function doesn't accept an object of the type provided"); +owner->dtor = [](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); }; +return meta_factory<Type>{}; +} + +/** + * @brief Assigns a meta data to a meta type. + * + * Both data members and static and global variables, as well as constants + * of any kind, can be assigned to a meta type.<br/> + * From a client's point of view, all the variables associated with the + * reflected object will appear as if they were part of the type itself. + * + * @tparam Data The actual variable to attach to the meta type. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return An extended meta factory for the parent type. + */ +template<auto Data, typename Policy = as_is_t> +auto data(const id_type id) ENTT_NOEXCEPT { +if constexpr(std::is_member_object_pointer_v<decltype(Data)>) { +using data_type = std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>; + +static internal::meta_data_node node{ +{}, +/* this is never static */ +std::is_const_v<data_type> ? internal::meta_traits::is_const : internal::meta_traits::is_none, +nullptr, +nullptr, +1u, +internal::meta_node<std::remove_const_t<data_type>>::resolve(), +&meta_arg<type_list<std::remove_const_t<data_type>>>, +&meta_setter<Type, Data>, +&meta_getter<Type, Data, Policy> +// tricks clang-format +}; + +link_data_if_required(id, node); +return meta_factory<Type, std::integral_constant<decltype(Data), Data>, std::integral_constant<decltype(Data), Data>>{&node.prop}; +} else { +using data_type = std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>; + +static internal::meta_data_node node{ +{}, +((std::is_same_v<Type, std::remove_const_t<data_type>> || std::is_const_v<data_type>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static, +nullptr, +nullptr, +1u, +internal::meta_node<std::remove_const_t<data_type>>::resolve(), +&meta_arg<type_list<std::remove_const_t<data_type>>>, +&meta_setter<Type, Data>, +&meta_getter<Type, Data, Policy> +// tricks clang-format +}; + +link_data_if_required(id, node); +return meta_factory<Type, std::integral_constant<decltype(Data), Data>>{&node.prop}; +} +} + +/** + * @brief Assigns a meta data to a meta type by means of its setter and + * getter. + * + * Setters and getters can be either free functions, member functions or a + * mix of them.<br/> + * In case of free functions, setters and getters must accept a reference to + * an instance of the parent type as their first argument. A setter has then + * an extra argument of a type convertible to that of the parameter to + * set.<br/> + * In case of member functions, getters have no arguments at all, while + * setters has an argument of a type convertible to that of the parameter to + * set. + * + * @tparam Setter The actual function to use as a setter. + * @tparam Getter The actual function to use as a getter. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return An extended meta factory for the parent type. + */ +template<auto Setter, auto Getter, typename Policy = as_is_t> +auto data(const id_type id) ENTT_NOEXCEPT { +using data_type = std::invoke_result_t<decltype(Getter), Type &>; +static_assert(Policy::template value<data_type>, "Invalid return type for the given policy"); + +if constexpr(std::is_same_v<decltype(Setter), std::nullptr_t>) { +static internal::meta_data_node node{ +{}, +/* this is never static */ +internal::meta_traits::is_const, +nullptr, +nullptr, +0u, +internal::meta_node<std::remove_cv_t<std::remove_reference_t<data_type>>>::resolve(), +&meta_arg<type_list<>>, +&meta_setter<Type, Setter>, +&meta_getter<Type, Getter, Policy> +// tricks clang-format +}; + +link_data_if_required(id, node); +return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop}; +} else { +using args_type = typename meta_function_helper_t<Type, decltype(Setter)>::args_type; + +static internal::meta_data_node node{ +{}, +/* this is never static nor const */ +internal::meta_traits::is_none, +nullptr, +nullptr, +1u, +internal::meta_node<std::remove_cv_t<std::remove_reference_t<data_type>>>::resolve(), +&meta_arg<type_list<type_list_element_t<args_type::size != 1u, args_type>>>, +&meta_setter<Type, Setter>, +&meta_getter<Type, Getter, Policy> +// tricks clang-format +}; + +link_data_if_required(id, node); +return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop}; +} +} + +/** + * @brief Assigns a meta data to a meta type by means of its setters and + * getter. + * + * Multi-setter support for meta data members. All setters are tried in the + * order of definition before returning to the caller.<br/> + * Setters can be either free functions, member functions or a mix of them + * and are provided via a `value_list` type. + * + * @sa data + * + * @tparam Setter The actual functions to use as setters. + * @tparam Getter The actual getter function. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return An extended meta factory for the parent type. + */ +template<typename Setter, auto Getter, typename Policy = as_is_t> +auto data(const id_type id) ENTT_NOEXCEPT { +return data<Setter, Getter, Policy>(id, std::make_index_sequence<Setter::size>{}); +} + +/** + * @brief Assigns a meta function to a meta type. + * + * Both member functions and free functions can be assigned to a meta + * type.<br/> + * From a client's point of view, all the functions associated with the + * reflected object will appear as if they were part of the type itself. + * + * @tparam Candidate The actual function to attach to the meta type. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return An extended meta factory for the parent type. + */ +template<auto Candidate, typename Policy = as_is_t> +auto func(const id_type id) ENTT_NOEXCEPT { +using descriptor = meta_function_helper_t<Type, decltype(Candidate)>; +static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy"); + +static internal::meta_func_node node{ +{}, +(descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none), +nullptr, +nullptr, +descriptor::args_type::size, +internal::meta_node<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>>>::resolve(), +&meta_arg<typename descriptor::args_type>, +&meta_invoke<Type, Candidate, Policy> +// tricks clang-format +}; + +link_func_if_required(id, node); +return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop}; +} + +private: +internal::meta_type_node *owner; +}; + +/** + * @brief Utility function to use for reflection. + * + * This is the point from which everything starts.<br/> + * By invoking this function with a type that is not yet reflected, a meta type + * is created to which it will be possible to attach meta objects through a + * dedicated factory. + * + * @tparam Type Type to reflect. + * @return A meta factory for the given type. + */ +template<typename Type> +[[nodiscard]] auto meta() ENTT_NOEXCEPT { +auto *const node = internal::meta_node<Type>::resolve(); +// extended meta factory to allow assigning properties to opaque meta types +return meta_factory<Type, Type>{&node->prop}; +} + +/** + * @brief Resets a type and all its parts. + * + * Resets a type and all its data members, member functions and properties, as + * well as its constructors, destructors and conversion functions if any.<br/> + * Base classes aren't reset but the link between the two types is removed. + * + * The type is also removed from the list of searchable types. + * + * @param id Unique identifier. + */ +inline void meta_reset(const id_type id) ENTT_NOEXCEPT { +auto clear_chain = [](auto **curr, auto... member) { +for(; *curr; *curr = std::exchange((*curr)->next, nullptr)) { +if constexpr(sizeof...(member) != 0u) { +static_assert(sizeof...(member) == 1u, "Assert in defense of the future me"); +for(auto **sub = (&((*curr)->*member), ...); *sub; *sub = std::exchange((*sub)->next, nullptr)) {} +} +} +}; + +for(auto **it = internal::meta_context::global(); *it; it = &(*it)->next) { +if(auto *node = *it; node->id == id) { +clear_chain(&node->prop); +clear_chain(&node->base); +clear_chain(&node->conv); +clear_chain(&node->ctor); +clear_chain(&node->data, &internal::meta_data_node::prop); +clear_chain(&node->func, &internal::meta_func_node::prop); + +node->id = {}; +node->dtor = nullptr; +*it = std::exchange(node->next, nullptr); + +break; +} +} +} + +/** + * @brief Resets a type and all its parts. + * + * @sa meta_reset + * + * @tparam Type Type to reset. + */ +template<typename Type> +void meta_reset() ENTT_NOEXCEPT { +meta_reset(internal::meta_node<Type>::resolve()->id); +} + +/** + * @brief Resets all searchable types. + * + * @sa meta_reset + */ +inline void meta_reset() ENTT_NOEXCEPT { +while(*internal::meta_context::global()) { +meta_reset((*internal::meta_context::global())->id); +} +} + +} // namespace entt + +#endif + +// #include "meta/meta.hpp" +#ifndef ENTT_META_META_HPP +#define ENTT_META_META_HPP + +#include <cstddef> +#include <iterator> +#include <memory> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/any.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" + +// #include "adl_pointer.hpp" + +// #include "ctx.hpp" + +// #include "fwd.hpp" + +// #include "node.hpp" + +// #include "range.hpp" + +// #include "type_traits.hpp" + + +namespace entt { + +class meta_any; +class meta_type; + +/*! @brief Proxy object for sequence containers. */ +class meta_sequence_container { +class meta_iterator; + +public: +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Meta iterator type. */ +using iterator = meta_iterator; + +/*! @brief Default constructor. */ +meta_sequence_container() ENTT_NOEXCEPT = default; + +/** + * @brief Construct a proxy object for sequence containers. + * @tparam Type Type of container to wrap. + * @param instance The container to wrap. + */ +template<typename Type> +meta_sequence_container(std::in_place_type_t<Type>, any instance) ENTT_NOEXCEPT +: value_type_node{internal::meta_node<std::remove_cv_t<std::remove_reference_t<typename Type::value_type>>>::resolve()}, +size_fn{&meta_sequence_container_traits<Type>::size}, +resize_fn{&meta_sequence_container_traits<Type>::resize}, +iter_fn{&meta_sequence_container_traits<Type>::iter}, +insert_fn{&meta_sequence_container_traits<Type>::insert}, +erase_fn{&meta_sequence_container_traits<Type>::erase}, +storage{std::move(instance)} {} + +[[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; +[[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; +inline bool resize(const size_type); +inline bool clear(); +[[nodiscard]] inline iterator begin(); +[[nodiscard]] inline iterator end(); +inline iterator insert(iterator, meta_any); +inline iterator erase(iterator); +[[nodiscard]] inline meta_any operator[](const size_type); +[[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; + +private: +internal::meta_type_node *value_type_node = nullptr; +size_type (*size_fn)(const any &) ENTT_NOEXCEPT = nullptr; +bool (*resize_fn)(any &, size_type) = nullptr; +iterator (*iter_fn)(any &, const bool) = nullptr; +iterator (*insert_fn)(any &, const std::ptrdiff_t, meta_any &) = nullptr; +iterator (*erase_fn)(any &, const std::ptrdiff_t) = nullptr; +any storage{}; +}; + +/*! @brief Proxy object for associative containers. */ +class meta_associative_container { +class meta_iterator; + +public: +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Meta iterator type. */ +using iterator = meta_iterator; + +/*! @brief Default constructor. */ +meta_associative_container() ENTT_NOEXCEPT = default; + +/** + * @brief Construct a proxy object for associative containers. + * @tparam Type Type of container to wrap. + * @param instance The container to wrap. + */ +template<typename Type> +meta_associative_container(std::in_place_type_t<Type>, any instance) ENTT_NOEXCEPT +: key_only_container{meta_associative_container_traits<Type>::key_only}, +key_type_node{internal::meta_node<std::remove_cv_t<std::remove_reference_t<typename Type::key_type>>>::resolve()}, +mapped_type_node{nullptr}, +value_type_node{internal::meta_node<std::remove_cv_t<std::remove_reference_t<typename Type::value_type>>>::resolve()}, +size_fn{&meta_associative_container_traits<Type>::size}, +clear_fn{&meta_associative_container_traits<Type>::clear}, +iter_fn{&meta_associative_container_traits<Type>::iter}, +insert_fn{&meta_associative_container_traits<Type>::insert}, +erase_fn{&meta_associative_container_traits<Type>::erase}, +find_fn{&meta_associative_container_traits<Type>::find}, +storage{std::move(instance)} { +if constexpr(!meta_associative_container_traits<Type>::key_only) { +mapped_type_node = internal::meta_node<std::remove_cv_t<std::remove_reference_t<typename Type::mapped_type>>>::resolve(); +} +} + +[[nodiscard]] inline bool key_only() const ENTT_NOEXCEPT; +[[nodiscard]] inline meta_type key_type() const ENTT_NOEXCEPT; +[[nodiscard]] inline meta_type mapped_type() const ENTT_NOEXCEPT; +[[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; +[[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; +inline bool clear(); +[[nodiscard]] inline iterator begin(); +[[nodiscard]] inline iterator end(); +inline bool insert(meta_any, meta_any); +inline bool erase(meta_any); +[[nodiscard]] inline iterator find(meta_any); +[[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; + +private: +bool key_only_container{}; +internal::meta_type_node *key_type_node = nullptr; +internal::meta_type_node *mapped_type_node = nullptr; +internal::meta_type_node *value_type_node = nullptr; +size_type (*size_fn)(const any &) ENTT_NOEXCEPT = nullptr; +bool (*clear_fn)(any &) = nullptr; +iterator (*iter_fn)(any &, const bool) = nullptr; +bool (*insert_fn)(any &, meta_any &, meta_any &) = nullptr; +bool (*erase_fn)(any &, meta_any &) = nullptr; +iterator (*find_fn)(any &, meta_any &) = nullptr; +any storage{}; +}; + +/*! @brief Opaque wrapper for values of any type. */ +class meta_any { +enum class operation : std::uint8_t { +deref, +seq, +assoc +}; + +using vtable_type = void(const operation, const any &, void *); + +template<typename Type> +static void basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const any &value, [[maybe_unused]] void *other) { +static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type"); + +if constexpr(!std::is_void_v<Type>) { +switch(op) { +case operation::deref: +if constexpr(is_meta_pointer_like_v<Type>) { +if constexpr(std::is_function_v<std::remove_const_t<typename std::pointer_traits<Type>::element_type>>) { +*static_cast<meta_any *>(other) = any_cast<Type>(value); +} else if constexpr(!std::is_same_v<std::remove_const_t<typename std::pointer_traits<Type>::element_type>, void>) { +using in_place_type = decltype(adl_meta_pointer_like<Type>::dereference(any_cast<const Type &>(value))); + +if constexpr(std::is_constructible_v<bool, Type>) { +if(const auto &pointer_like = any_cast<const Type &>(value); pointer_like) { +static_cast<meta_any *>(other)->emplace<in_place_type>(adl_meta_pointer_like<Type>::dereference(pointer_like)); +} +} else { +static_cast<meta_any *>(other)->emplace<in_place_type>(adl_meta_pointer_like<Type>::dereference(any_cast<const Type &>(value))); +} +} +} +break; +case operation::seq: +if constexpr(is_complete_v<meta_sequence_container_traits<Type>>) { +*static_cast<meta_sequence_container *>(other) = {std::in_place_type<Type>, std::move(const_cast<any &>(value))}; +} +break; +case operation::assoc: +if constexpr(is_complete_v<meta_associative_container_traits<Type>>) { +*static_cast<meta_associative_container *>(other) = {std::in_place_type<Type>, std::move(const_cast<any &>(value))}; +} +break; +} +} +} + +void release() { +if(node && node->dtor && storage.owner()) { +node->dtor(storage.data()); +} +} + +meta_any(const meta_any &other, any ref) ENTT_NOEXCEPT +: storage{std::move(ref)}, +node{storage ? other.node : nullptr}, +vtable{storage ? other.vtable : &basic_vtable<void>} {} + +public: +/*! @brief Default constructor. */ +meta_any() ENTT_NOEXCEPT +: storage{}, +node{}, +vtable{&basic_vtable<void>} {} + +/** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ +template<typename Type, typename... Args> +explicit meta_any(std::in_place_type_t<Type>, Args &&...args) +: storage{std::in_place_type<Type>, std::forward<Args>(args)...}, +node{internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve()}, +vtable{&basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>} {} + +/** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ +template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>>> +meta_any(Type &&value) +: meta_any{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)} {} + +/** + * @brief Copy constructor. + * @param other The instance to copy from. + */ +meta_any(const meta_any &other) = default; + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +meta_any(meta_any &&other) ENTT_NOEXCEPT +: storage{std::move(other.storage)}, +node{std::exchange(other.node, nullptr)}, +vtable{std::exchange(other.vtable, &basic_vtable<void>)} {} + +/*! @brief Frees the internal storage, whatever it means. */ +~meta_any() { +release(); +} + +/** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This meta any object. + */ +meta_any &operator=(const meta_any &other) { +release(); +vtable = other.vtable; +storage = other.storage; +node = other.node; +return *this; +} + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This meta any object. + */ +meta_any &operator=(meta_any &&other) ENTT_NOEXCEPT { +release(); +vtable = std::exchange(other.vtable, &basic_vtable<void>); +storage = std::move(other.storage); +node = std::exchange(other.node, nullptr); +return *this; +} + +/** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This meta any object. + */ +template<typename Type> +std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>, meta_any &> +operator=(Type &&value) { +emplace<std::decay_t<Type>>(std::forward<Type>(value)); +return *this; +} + +/*! @copydoc any::type */ +[[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; + +/*! @copydoc any::data */ +[[nodiscard]] const void *data() const ENTT_NOEXCEPT { +return storage.data(); +} + +/*! @copydoc any::data */ +[[nodiscard]] void *data() ENTT_NOEXCEPT { +return storage.data(); +} + +/** + * @brief Invokes the underlying function, if possible. + * + * @sa meta_func::invoke + * + * @tparam Args Types of arguments to use to invoke the function. + * @param id Unique identifier. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ +template<typename... Args> +meta_any invoke(const id_type id, Args &&...args) const; + +/*! @copydoc invoke */ +template<typename... Args> +meta_any invoke(const id_type id, Args &&...args); + +/** + * @brief Sets the value of a given variable. + * + * The type of the value is such that a cast or conversion to the type of + * the variable is possible. Otherwise, invoking the setter does nothing. + * + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ +template<typename Type> +bool set(const id_type id, Type &&value); + +/** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @return A wrapper containing the value of the underlying variable. + */ +[[nodiscard]] meta_any get(const id_type id) const; + +/*! @copydoc get */ +[[nodiscard]] meta_any get(const id_type id); + +/** + * @brief Tries to cast an instance to a given type. + * @tparam Type Type to which to cast the instance. + * @return A (possibly null) pointer to the contained instance. + */ +template<typename Type> +[[nodiscard]] const Type *try_cast() const { +if(const auto &info = type_id<Type>(); node && *node->info == info) { +return any_cast<Type>(&storage); +} else if(node) { +for(auto *it = node->base; it; it = it->next) { +const auto as_const = it->cast(as_ref()); + +if(const Type *base = as_const.template try_cast<Type>(); base) { +return base; +} +} +} + +return nullptr; +} + +/*! @copydoc try_cast */ +template<typename Type> +[[nodiscard]] Type *try_cast() { +if(const auto &info = type_id<Type>(); node && *node->info == info) { +return any_cast<Type>(&storage); +} else if(node) { +for(auto *it = node->base; it; it = it->next) { +if(Type *base = it->cast(as_ref()).template try_cast<Type>(); base) { +return base; +} +} +} + +return nullptr; +} + +/** + * @brief Tries to cast an instance to a given type. + * + * The type of the instance must be such that the cast is possible. + * + * @warning + * Attempting to perform an invalid cast results is undefined behavior. + * + * @tparam Type Type to which to cast the instance. + * @return A reference to the contained instance. + */ +template<typename Type> +[[nodiscard]] Type cast() const { +auto *const instance = try_cast<std::remove_reference_t<Type>>(); +ENTT_ASSERT(instance, "Invalid instance"); +return static_cast<Type>(*instance); +} + +/*! @copydoc cast */ +template<typename Type> +[[nodiscard]] Type cast() { +// forces const on non-reference types to make them work also with wrappers for const references +auto *const instance = try_cast<std::remove_reference_t<const Type>>(); +ENTT_ASSERT(instance, "Invalid instance"); +return static_cast<Type>(*instance); +} + +/** + * @brief Converts an object in such a way that a given cast becomes viable. + * @param type Meta type to which the cast is requested. + * @return A valid meta any object if there exists a viable conversion, an + * invalid one otherwise. + */ +[[nodiscard]] meta_any allow_cast(const meta_type &type) const; + +/** + * @brief Converts an object in such a way that a given cast becomes viable. + * @param type Meta type to which the cast is requested. + * @return True if there exists a viable conversion, false otherwise. + */ +[[nodiscard]] bool allow_cast(const meta_type &type) { +if(auto other = std::as_const(*this).allow_cast(type); other) { +if(other.storage.owner()) { +std::swap(*this, other); +} + +return true; +} + +return false; +} + +/** + * @brief Converts an object in such a way that a given cast becomes viable. + * @tparam Type Type to which the cast is requested. + * @return A valid meta any object if there exists a viable conversion, an + * invalid one otherwise. + */ +template<typename Type> +[[nodiscard]] meta_any allow_cast() const { +const auto other = allow_cast(internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve()); + +if constexpr(std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>) { +return other.storage.owner() ? other : meta_any{}; +} else { +return other; +} +} + +/** + * @brief Converts an object in such a way that a given cast becomes viable. + * @tparam Type Type to which the cast is requested. + * @return True if there exists a viable conversion, false otherwise. + */ +template<typename Type> +bool allow_cast() { +if(auto other = std::as_const(*this).allow_cast(internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve()); other) { +if(other.storage.owner()) { +std::swap(*this, other); +return true; +} + +return (static_cast<constness_as_t<any, std::remove_reference_t<const Type>> &>(storage).data() != nullptr); +} + +return false; +} + +/*! @copydoc any::emplace */ +template<typename Type, typename... Args> +void emplace(Args &&...args) { +release(); +vtable = &basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>; +storage.emplace<Type>(std::forward<Args>(args)...); +node = internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve(); +} + +/*! @copydoc any::assign */ +bool assign(const meta_any &other); + +/*! @copydoc any::assign */ +bool assign(meta_any &&other); + +/*! @copydoc any::reset */ +void reset() { +release(); +vtable = &basic_vtable<void>; +storage.reset(); +node = nullptr; +} + +/** + * @brief Returns a sequence container proxy. + * @return A sequence container proxy for the underlying object. + */ +[[nodiscard]] meta_sequence_container as_sequence_container() ENTT_NOEXCEPT { +any detached = storage.as_ref(); +meta_sequence_container proxy; +vtable(operation::seq, detached, &proxy); +return proxy; +} + +/*! @copydoc as_sequence_container */ +[[nodiscard]] meta_sequence_container as_sequence_container() const ENTT_NOEXCEPT { +any detached = storage.as_ref(); +meta_sequence_container proxy; +vtable(operation::seq, detached, &proxy); +return proxy; +} + +/** + * @brief Returns an associative container proxy. + * @return An associative container proxy for the underlying object. + */ +[[nodiscard]] meta_associative_container as_associative_container() ENTT_NOEXCEPT { +any detached = storage.as_ref(); +meta_associative_container proxy; +vtable(operation::assoc, detached, &proxy); +return proxy; +} + +/*! @copydoc as_associative_container */ +[[nodiscard]] meta_associative_container as_associative_container() const ENTT_NOEXCEPT { +any detached = storage.as_ref(); +meta_associative_container proxy; +vtable(operation::assoc, detached, &proxy); +return proxy; +} + +/** + * @brief Indirection operator for dereferencing opaque objects. + * @return A wrapper that shares a reference to an unmanaged object if the + * wrapped element is dereferenceable, an invalid meta any otherwise. + */ +[[nodiscard]] meta_any operator*() const ENTT_NOEXCEPT { +meta_any ret{}; +vtable(operation::deref, storage, &ret); +return ret; +} + +/** + * @brief Returns false if a wrapper is invalid, true otherwise. + * @return False if the wrapper is invalid, true otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return !(node == nullptr); +} + +/*! @copydoc any::operator== */ +[[nodiscard]] bool operator==(const meta_any &other) const { +return (!node && !other.node) || (node && other.node && *node->info == *other.node->info && storage == other.storage); +} + +/*! @copydoc any::as_ref */ +[[nodiscard]] meta_any as_ref() ENTT_NOEXCEPT { +return meta_any{*this, storage.as_ref()}; +} + +/*! @copydoc any::as_ref */ +[[nodiscard]] meta_any as_ref() const ENTT_NOEXCEPT { +return meta_any{*this, storage.as_ref()}; +} + +/*! @copydoc any::owner */ +[[nodiscard]] bool owner() const ENTT_NOEXCEPT { +return storage.owner(); +} + +private: +any storage; +internal::meta_type_node *node; +vtable_type *vtable; +}; + +/** + * @brief Checks if two wrappers differ in their content. + * @param lhs A wrapper, either empty or not. + * @param rhs A wrapper, either empty or not. + * @return True if the two wrappers differ in their content, false otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_any &lhs, const meta_any &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template<typename Type, typename... Args> +meta_any make_meta(Args &&...args) { +return meta_any{std::in_place_type<Type>, std::forward<Args>(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template<typename Type> +meta_any forward_as_meta(Type &&value) { +return meta_any{std::in_place_type<std::conditional_t<std::is_rvalue_reference_v<Type>, std::decay_t<Type>, Type>>, std::forward<Type>(value)}; +} + +/** + * @brief Opaque pointers to instances of any type. + * + * A handle doesn't perform copies and isn't responsible for the contained + * object. It doesn't prolong the lifetime of the pointed instance.<br/> + * Handles are used to generate references to actual objects when needed. + */ +struct meta_handle { +/*! @brief Default constructor. */ +meta_handle() = default; + +/*! @brief Default copy constructor, deleted on purpose. */ +meta_handle(const meta_handle &) = delete; + +/*! @brief Default move constructor. */ +meta_handle(meta_handle &&) = default; + +/** + * @brief Default copy assignment operator, deleted on purpose. + * @return This meta handle. + */ +meta_handle &operator=(const meta_handle &) = delete; + +/** + * @brief Default move assignment operator. + * @return This meta handle. + */ +meta_handle &operator=(meta_handle &&) = default; + +/** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param value An instance of an object to use to initialize the handle. + */ +template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_handle>>> +meta_handle(Type &value) ENTT_NOEXCEPT +: meta_handle{} { +if constexpr(std::is_same_v<std::decay_t<Type>, meta_any>) { +any = value.as_ref(); +} else { +any.emplace<Type &>(value); +} +} + +/** + * @brief Returns false if a handle is invalid, true otherwise. + * @return False if the handle is invalid, true otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(any); +} + +/** + * @brief Access operator for accessing the contained opaque object. + * @return A wrapper that shares a reference to an unmanaged object. + */ +[[nodiscard]] meta_any *operator->() { +return &any; +} + +/*! @copydoc operator-> */ +[[nodiscard]] const meta_any *operator->() const { +return &any; +} + +private: +meta_any any; +}; + +/*! @brief Opaque wrapper for properties of any type. */ +struct meta_prop { +/*! @brief Node type. */ +using node_type = internal::meta_prop_node; + +/** + * @brief Constructs an instance from a given node. + * @param curr The underlying node with which to construct the instance. + */ +meta_prop(const node_type *curr = nullptr) ENTT_NOEXCEPT +: node{curr} {} + +/** + * @brief Returns the stored key as a const reference. + * @return A wrapper containing the key stored with the property. + */ +[[nodiscard]] meta_any key() const { +return node->id.as_ref(); +} + +/** + * @brief Returns the stored value by copy. + * @return A wrapper containing the value stored with the property. + */ +[[nodiscard]] meta_any value() const { +return node->value; +} + +/** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return !(node == nullptr); +} + +private: +const node_type *node; +}; + +/*! @brief Opaque wrapper for data members. */ +struct meta_data { +/*! @brief Node type. */ +using node_type = internal::meta_data_node; +/*! @brief Unsigned integer type. */ +using size_type = typename node_type::size_type; + +/*! @copydoc meta_prop::meta_prop */ +meta_data(const node_type *curr = nullptr) ENTT_NOEXCEPT +: node{curr} {} + +/*! @copydoc meta_type::id */ +[[nodiscard]] id_type id() const ENTT_NOEXCEPT { +return node->id; +} + +/** + * @brief Returns the number of setters available. + * @return The number of setters available. + */ +[[nodiscard]] size_type arity() const ENTT_NOEXCEPT { +return node->arity; +} + +/** + * @brief Indicates whether a data member is constant or not. + * @return True if the data member is constant, false otherwise. + */ +[[nodiscard]] bool is_const() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_const); +} + +/** + * @brief Indicates whether a data member is static or not. + * @return True if the data member is static, false otherwise. + */ +[[nodiscard]] bool is_static() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_static); +} + +/*! @copydoc meta_any::type */ +[[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; + +/** + * @brief Sets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the data + * member.<br/> + * The type of the value is such that a cast or conversion to the type of + * the variable is possible. Otherwise, invoking the setter does nothing. + * + * @tparam Type Type of value to assign. + * @param instance An opaque instance of the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ +template<typename Type> +bool set(meta_handle instance, Type &&value) const { +return node->set && node->set(std::move(instance), std::forward<Type>(value)); +} + +/** + * @brief Gets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the data + * member. + * + * @param instance An opaque instance of the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ +[[nodiscard]] meta_any get(meta_handle instance) const { +return node->get(std::move(instance)); +} + +/** + * @brief Returns the type accepted by the i-th setter. + * @param index Index of the setter of which to return the accepted type. + * @return The type accepted by the i-th setter. + */ +[[nodiscard]] inline meta_type arg(const size_type index) const ENTT_NOEXCEPT; + +/** + * @brief Returns a range to visit registered meta properties. + * @return An iterable range to visit registered meta properties. + */ +[[nodiscard]] meta_range<meta_prop> prop() const ENTT_NOEXCEPT { +return node->prop; +} + +/** + * @brief Lookup function for registered meta properties. + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ +[[nodiscard]] meta_prop prop(meta_any key) const { +for(auto curr: prop()) { +if(curr.key() == key) { +return curr; +} +} + +return nullptr; +} + +/** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return !(node == nullptr); +} + +private: +const node_type *node; +}; + +/*! @brief Opaque wrapper for member functions. */ +struct meta_func { +/*! @brief Node type. */ +using node_type = internal::meta_func_node; +/*! @brief Unsigned integer type. */ +using size_type = typename node_type::size_type; + +/*! @copydoc meta_prop::meta_prop */ +meta_func(const node_type *curr = nullptr) ENTT_NOEXCEPT +: node{curr} {} + +/*! @copydoc meta_type::id */ +[[nodiscard]] id_type id() const ENTT_NOEXCEPT { +return node->id; +} + +/** + * @brief Returns the number of arguments accepted by a member function. + * @return The number of arguments accepted by the member function. + */ +[[nodiscard]] size_type arity() const ENTT_NOEXCEPT { +return node->arity; +} + +/** + * @brief Indicates whether a member function is constant or not. + * @return True if the member function is constant, false otherwise. + */ +[[nodiscard]] bool is_const() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_const); +} + +/** + * @brief Indicates whether a member function is static or not. + * @return True if the member function is static, false otherwise. + */ +[[nodiscard]] bool is_static() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_static); +} + +/** + * @brief Returns the return type of a member function. + * @return The return type of the member function. + */ +[[nodiscard]] inline meta_type ret() const ENTT_NOEXCEPT; + +/** + * @brief Returns the type of the i-th argument of a member function. + * @param index Index of the argument of which to return the type. + * @return The type of the i-th argument of a member function. + */ +[[nodiscard]] inline meta_type arg(const size_type index) const ENTT_NOEXCEPT; + +/** + * @brief Invokes the underlying function, if possible. + * + * To invoke a member function, the parameters must be such that a cast or + * conversion to the required types is possible. Otherwise, an empty and + * thus invalid wrapper is returned.<br/> + * It must be possible to cast the instance to the parent type of the member + * function. + * + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ +meta_any invoke(meta_handle instance, meta_any *const args, const size_type sz) const { +return sz == arity() ? node->invoke(std::move(instance), args) : meta_any{}; +} + +/** + * @copybrief invoke + * + * @sa invoke + * + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the new instance, if any. + */ +template<typename... Args> +meta_any invoke(meta_handle instance, Args &&...args) const { +meta_any arguments[sizeof...(Args) + 1u]{std::forward<Args>(args)...}; +return invoke(std::move(instance), arguments, sizeof...(Args)); +} + +/*! @copydoc meta_data::prop */ +[[nodiscard]] meta_range<meta_prop> prop() const ENTT_NOEXCEPT { +return node->prop; +} + +/** + * @brief Lookup function for registered meta properties. + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ +[[nodiscard]] meta_prop prop(meta_any key) const { +for(auto curr: prop()) { +if(curr.key() == key) { +return curr; +} +} + +return nullptr; +} + +/** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return !(node == nullptr); +} + +private: +const node_type *node; +}; + +/*! @brief Opaque wrapper for types. */ +class meta_type { +template<auto Member, typename Pred> +[[nodiscard]] std::decay_t<decltype(std::declval<internal::meta_type_node>().*Member)> lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, Pred pred) const { +std::decay_t<decltype(node->*Member)> candidate{}; +size_type extent{sz + 1u}; +bool ambiguous{}; + +for(auto *curr = (node->*Member); curr; curr = curr->next) { +if(pred(curr) && curr->arity == sz) { +size_type direct{}; +size_type ext{}; + +for(size_type next{}; next < sz && next == (direct + ext) && args[next]; ++next) { +const auto type = args[next].type(); +const auto other = curr->arg(next); + +if(const auto &info = other.info(); info == type.info()) { +++direct; +} else { +ext += internal::find_by<&node_type::base>(info, type.node) +|| internal::find_by<&node_type::conv>(info, type.node) +|| (type.node->conversion_helper && other.node->conversion_helper); +} +} + +if((direct + ext) == sz) { +if(ext < extent) { +candidate = curr; +extent = ext; +ambiguous = false; +} else if(ext == extent) { +ambiguous = true; +} +} +} +} + +return (candidate && !ambiguous) ? candidate : decltype(candidate){}; +} + +public: +/*! @brief Node type. */ +using node_type = internal::meta_type_node; +/*! @brief Node type. */ +using base_node_type = internal::meta_base_node; +/*! @brief Unsigned integer type. */ +using size_type = typename node_type::size_type; + +/*! @copydoc meta_prop::meta_prop */ +meta_type(const node_type *curr = nullptr) ENTT_NOEXCEPT +: node{curr} {} + +/** + * @brief Constructs an instance from a given base node. + * @param curr The base node with which to construct the instance. + */ +meta_type(const base_node_type *curr) ENTT_NOEXCEPT +: node{curr ? curr->type : nullptr} {} + +/** + * @brief Returns the type info object of the underlying type. + * @return The type info object of the underlying type. + */ +[[nodiscard]] const type_info &info() const ENTT_NOEXCEPT { +return *node->info; +} + +/** + * @brief Returns the identifier assigned to a type. + * @return The identifier assigned to the type. + */ +[[nodiscard]] id_type id() const ENTT_NOEXCEPT { +return node->id; +} + +/** + * @brief Returns the size of the underlying type if known. + * @return The size of the underlying type if known, 0 otherwise. + */ +[[nodiscard]] size_type size_of() const ENTT_NOEXCEPT { +return node->size_of; +} + +/** + * @brief Checks whether a type refers to an arithmetic type or not. + * @return True if the underlying type is an arithmetic type, false + * otherwise. + */ +[[nodiscard]] bool is_arithmetic() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_arithmetic); +} + +/** + * @brief Checks whether a type refers to an array type or not. + * @return True if the underlying type is an array type, false otherwise. + */ +[[nodiscard]] bool is_array() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_array); +} + +/** + * @brief Checks whether a type refers to an enum or not. + * @return True if the underlying type is an enum, false otherwise. + */ +[[nodiscard]] bool is_enum() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_enum); +} + +/** + * @brief Checks whether a type refers to a class or not. + * @return True if the underlying type is a class, false otherwise. + */ +[[nodiscard]] bool is_class() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_class); +} + +/** + * @brief Checks whether a type refers to a pointer or not. + * @return True if the underlying type is a pointer, false otherwise. + */ +[[nodiscard]] bool is_pointer() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_pointer); +} + +/** + * @brief Checks whether a type is a pointer-like type or not. + * @return True if the underlying type is a pointer-like one, false + * otherwise. + */ +[[nodiscard]] bool is_pointer_like() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_meta_pointer_like); +} + +/** + * @brief Checks whether a type refers to a sequence container or not. + * @return True if the type is a sequence container, false otherwise. + */ +[[nodiscard]] bool is_sequence_container() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_meta_sequence_container); +} + +/** + * @brief Checks whether a type refers to an associative container or not. + * @return True if the type is an associative container, false otherwise. + */ +[[nodiscard]] bool is_associative_container() const ENTT_NOEXCEPT { +return !!(node->traits & internal::meta_traits::is_meta_associative_container); +} + +/** + * @brief Checks whether a type refers to a recognized class template + * specialization or not. + * @return True if the type is a recognized class template specialization, + * false otherwise. + */ +[[nodiscard]] bool is_template_specialization() const ENTT_NOEXCEPT { +return (node->templ != nullptr); +} + +/** + * @brief Returns the number of template arguments. + * @return The number of template arguments. + */ +[[nodiscard]] size_type template_arity() const ENTT_NOEXCEPT { +return node->templ ? node->templ->arity : size_type{}; +} + +/** + * @brief Returns a tag for the class template of the underlying type. + * + * @sa meta_class_template_tag + * + * @return The tag for the class template of the underlying type. + */ +[[nodiscard]] inline meta_type template_type() const ENTT_NOEXCEPT { +return node->templ ? node->templ->type : meta_type{}; +} + +/** + * @brief Returns the type of the i-th template argument of a type. + * @param index Index of the template argument of which to return the type. + * @return The type of the i-th template argument of a type. + */ +[[nodiscard]] inline meta_type template_arg(const size_type index) const ENTT_NOEXCEPT { +return index < template_arity() ? node->templ->arg(index) : meta_type{}; +} + +/** + * @brief Returns a range to visit registered top-level base meta types. + * @return An iterable range to visit registered top-level base meta types. + */ +[[nodiscard]] meta_range<meta_type, internal::meta_base_node> base() const ENTT_NOEXCEPT { +return node->base; +} + +/** + * @brief Lookup function for registered base meta types. + * @param id Unique identifier. + * @return The registered base meta type for the given identifier, if any. + */ +[[nodiscard]] meta_type base(const id_type id) const { +return internal::find_by<&node_type::base>(id, node); +} + +/** + * @brief Returns a range to visit registered top-level meta data. + * @return An iterable range to visit registered top-level meta data. + */ +[[nodiscard]] meta_range<meta_data> data() const ENTT_NOEXCEPT { +return node->data; +} + +/** + * @brief Lookup function for registered meta data. + * + * Registered meta data of base classes will also be visited. + * + * @param id Unique identifier. + * @return The registered meta data for the given identifier, if any. + */ +[[nodiscard]] meta_data data(const id_type id) const { +return internal::find_by<&node_type::data>(id, node); +} + +/** + * @brief Returns a range to visit registered top-level functions. + * @return An iterable range to visit registered top-level functions. + */ +[[nodiscard]] meta_range<meta_func> func() const ENTT_NOEXCEPT { +return node->func; +} + +/** + * @brief Lookup function for registered meta functions. + * + * Registered meta functions of base classes will also be visited.<br/> + * In case of overloaded functions, the first one with the required + * identifier will be returned. + * + * @param id Unique identifier. + * @return The registered meta function for the given identifier, if any. + */ +[[nodiscard]] meta_func func(const id_type id) const { +return internal::find_by<&node_type::func>(id, node); +} + +/** + * @brief Creates an instance of the underlying type, if possible. + * + * Parameters are such that a cast or conversion to the required types is + * possible. Otherwise, an empty and thus invalid wrapper is returned.<br/> + * If suitable, the implicitly generated default constructor is used. + * + * @param args Parameters to use to construct the instance. + * @param sz Number of parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ +[[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const { +const auto *candidate = lookup<&node_type::ctor>(args, sz, [](const auto *) { return true; }); +return candidate ? candidate->invoke(args) : ((!sz && node->default_constructor) ? node->default_constructor() : meta_any{}); +} + +/** + * @copybrief construct + * + * @sa construct + * + * @tparam Args Types of arguments to use to construct the instance. + * @param args Parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ +template<typename... Args> +[[nodiscard]] meta_any construct(Args &&...args) const { +meta_any arguments[sizeof...(Args) + 1u]{std::forward<Args>(args)...}; +return construct(arguments, sizeof...(Args)); +} + +/** + * @brief Invokes a function given an identifier, if possible. + * + * It must be possible to cast the instance to the parent type of the member + * function. + * + * @sa meta_func::invoke + * + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ +meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const { +const auto *candidate = lookup<&node_type::func>(args, sz, [id](const auto *curr) { return curr->id == id; }); + +for(auto it = base().begin(), last = base().end(); it != last && !candidate; ++it) { +candidate = it->lookup<&node_type::func>(args, sz, [id](const auto *curr) { return curr->id == id; }); +} + +return candidate ? candidate->invoke(std::move(instance), args) : meta_any{}; +} + +/** + * @copybrief invoke + * + * @sa invoke + * + * @param id Unique identifier. + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the new instance, if any. + */ +template<typename... Args> +meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const { +meta_any arguments[sizeof...(Args) + 1u]{std::forward<Args>(args)...}; +return invoke(id, std::move(instance), arguments, sizeof...(Args)); +} + +/** + * @brief Sets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the data + * member.<br/> + * The type of the value is such that a cast or conversion to the type of + * the variable is possible. Otherwise, invoking the setter does nothing. + * + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ +template<typename Type> +bool set(const id_type id, meta_handle instance, Type &&value) const { +const auto candidate = data(id); +return candidate && candidate.set(std::move(instance), std::forward<Type>(value)); +} + +/** + * @brief Gets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the data + * member. + * + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ +[[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { +const auto candidate = data(id); +return candidate ? candidate.get(std::move(instance)) : meta_any{}; +} + +/** + * @brief Returns a range to visit registered top-level meta properties. + * @return An iterable range to visit registered top-level meta properties. + */ +[[nodiscard]] meta_range<meta_prop> prop() const ENTT_NOEXCEPT { +return node->prop; +} + +/** + * @brief Lookup function for meta properties. + * + * Properties of base classes are also visited. + * + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ +[[nodiscard]] meta_prop prop(meta_any key) const { +return internal::find_by<&internal::meta_type_node::prop>(key, node); +} + +/** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +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_type &other) const ENTT_NOEXCEPT { +return (!node && !other.node) || (node && other.node && *node->info == *other.node->info); +} + +private: +const node_type *node; +}; + +/** + * @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_type &lhs, const meta_type &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +[[nodiscard]] inline meta_type meta_any::type() const ENTT_NOEXCEPT { +return node; +} + +template<typename... Args> +meta_any meta_any::invoke(const id_type id, Args &&...args) const { +return type().invoke(id, *this, std::forward<Args>(args)...); +} + +template<typename... Args> +meta_any meta_any::invoke(const id_type id, Args &&...args) { +return type().invoke(id, *this, std::forward<Args>(args)...); +} + +template<typename Type> +bool meta_any::set(const id_type id, Type &&value) { +return type().set(id, *this, std::forward<Type>(value)); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) const { +return type().get(id, *this); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) { +return type().get(id, *this); +} + +[[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const { +if(const auto &info = type.info(); node && *node->info == info) { +return as_ref(); +} else if(node) { +for(auto *it = node->conv; it; it = it->next) { +if(*it->type->info == info) { +return it->conv(*this); +} +} + +if(node->conversion_helper && (type.is_arithmetic() || type.is_enum())) { +// exploits the fact that arithmetic types and enums are also default constructible +auto other = type.construct(); +ENTT_ASSERT(other.node->conversion_helper, "Conversion helper not found"); +const auto value = node->conversion_helper(nullptr, storage.data()); +other.node->conversion_helper(other.storage.data(), &value); +return other; +} + +for(auto *it = node->base; it; it = it->next) { +const auto as_const = it->cast(as_ref()); + +if(auto other = as_const.allow_cast(type); other) { +return other; +} +} +} + +return {}; +} + +inline bool meta_any::assign(const meta_any &other) { +auto value = other.allow_cast(node); +return value && storage.assign(std::move(value.storage)); +} + +inline bool meta_any::assign(meta_any &&other) { +if(*node->info == *other.node->info) { +return storage.assign(std::move(other.storage)); +} + +return assign(std::as_const(other)); +} + +[[nodiscard]] inline meta_type meta_data::type() const ENTT_NOEXCEPT { +return node->type; +} + +[[nodiscard]] inline meta_type meta_func::ret() const ENTT_NOEXCEPT { +return node->ret; +} + +[[nodiscard]] inline meta_type meta_data::arg(const size_type index) const ENTT_NOEXCEPT { +return index < arity() ? node->arg(index) : meta_type{}; +} + +[[nodiscard]] inline meta_type meta_func::arg(const size_type index) const ENTT_NOEXCEPT { +return index < arity() ? node->arg(index) : meta_type{}; +} + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +class meta_sequence_container::meta_iterator final { +friend class meta_sequence_container; + +using deref_fn_type = meta_any(const any &, const std::ptrdiff_t); + +template<typename It> +static meta_any deref_fn(const any &value, const std::ptrdiff_t pos) { +return meta_any{std::in_place_type<typename std::iterator_traits<It>::reference>, any_cast<const It &>(value)[pos]}; +} + +public: +using difference_type = std::ptrdiff_t; +using value_type = meta_any; +using pointer = input_iterator_pointer<value_type>; +using reference = value_type; +using iterator_category = std::input_iterator_tag; + +meta_iterator() ENTT_NOEXCEPT +: deref{}, +offset{}, +handle{} {} + +template<typename It> +explicit meta_iterator(It iter, const difference_type init) ENTT_NOEXCEPT +: deref{&deref_fn<It>}, +offset{init}, +handle{std::move(iter)} {} + +meta_iterator &operator++() ENTT_NOEXCEPT { +return ++offset, *this; +} + +meta_iterator operator++(int value) ENTT_NOEXCEPT { +meta_iterator orig = *this; +offset += ++value; +return orig; +} + +meta_iterator &operator--() ENTT_NOEXCEPT { +return --offset, *this; +} + +meta_iterator operator--(int value) ENTT_NOEXCEPT { +meta_iterator orig = *this; +offset -= ++value; +return orig; +} + +[[nodiscard]] reference operator*() const { +return deref(handle, offset); +} + +[[nodiscard]] pointer operator->() const { +return operator*(); +} + +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(handle); +} + +[[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { +return offset == other.offset; +} + +[[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { +return !(*this == other); +} + +private: +deref_fn_type *deref; +difference_type offset; +any handle; +}; + +class meta_associative_container::meta_iterator final { +enum class operation : std::uint8_t { +incr, +deref +}; + +using vtable_type = void(const operation, const any &, std::pair<meta_any, meta_any> *); + +template<bool KeyOnly, typename It> +static void basic_vtable(const operation op, const any &value, std::pair<meta_any, meta_any> *other) { +switch(op) { +case operation::incr: +++any_cast<It &>(const_cast<any &>(value)); +break; +case operation::deref: +const auto &it = any_cast<const It &>(value); +if constexpr(KeyOnly) { +other->first.emplace<decltype(*it)>(*it); +} else { +other->first.emplace<decltype((it->first))>(it->first); +other->second.emplace<decltype((it->second))>(it->second); +} +break; +} +} + +public: +using difference_type = std::ptrdiff_t; +using value_type = std::pair<meta_any, meta_any>; +using pointer = input_iterator_pointer<value_type>; +using reference = value_type; +using iterator_category = std::input_iterator_tag; + +meta_iterator() ENTT_NOEXCEPT +: vtable{}, +handle{} {} + +template<bool KeyOnly, typename It> +meta_iterator(std::integral_constant<bool, KeyOnly>, It iter) ENTT_NOEXCEPT +: vtable{&basic_vtable<KeyOnly, It>}, +handle{std::move(iter)} {} + +meta_iterator &operator++() ENTT_NOEXCEPT { +vtable(operation::incr, handle, nullptr); +return *this; +} + +meta_iterator operator++(int) ENTT_NOEXCEPT { +meta_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] reference operator*() const { +reference other; +vtable(operation::deref, handle, &other); +return other; +} + +[[nodiscard]] pointer operator->() const { +return operator*(); +} + +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(handle); +} + +[[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { +return handle == other.handle; +} + +[[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { +return !(*this == other); +} + +private: +vtable_type *vtable; +any handle; +}; + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Returns the meta value type of a container. + * @return The meta value type of the container. + */ +[[nodiscard]] inline meta_type meta_sequence_container::value_type() const ENTT_NOEXCEPT { +return value_type_node; +} + +/** + * @brief Returns the size of a container. + * @return The size of the container. + */ +[[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const ENTT_NOEXCEPT { +return size_fn(storage); +} + +/** + * @brief Resizes a container to contain a given number of elements. + * @param sz The new size of the container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::resize(const size_type sz) { +return resize_fn(storage, sz); +} + +/** + * @brief Clears the content of a container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::clear() { +return resize_fn(storage, 0u); +} + +/** + * @brief Returns an iterator to the first element of a container. + * @return An iterator to the first element of the container. + */ +[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() { +return iter_fn(storage, false); +} + +/** + * @brief Returns an iterator that is past the last element of a container. + * @return An iterator that is past the last element of the container. + */ +[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() { +return iter_fn(storage, true); +} + +/** + * @brief Inserts an element at a specified location of a container. + * @param it Iterator before which the element will be inserted. + * @param value Element value to insert. + * @return A possibly invalid iterator to the inserted element. + */ +inline meta_sequence_container::iterator meta_sequence_container::insert(iterator it, meta_any value) { +return insert_fn(storage, it.offset, value); +} + +/** + * @brief Removes a given element from a container. + * @param it Iterator to the element to remove. + * @return A possibly invalid iterator following the last removed element. + */ +inline meta_sequence_container::iterator meta_sequence_container::erase(iterator it) { +return erase_fn(storage, it.offset); +} + +/** + * @brief Returns a reference to the element at a given location of a container + * (no bounds checking is performed). + * @param pos The position of the element to return. + * @return A reference to the requested element properly wrapped. + */ +[[nodiscard]] inline meta_any meta_sequence_container::operator[](const size_type pos) { +auto it = begin(); +it.operator++(static_cast<int>(pos) - 1); +return *it; +} + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_sequence_container::operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(storage); +} + +/** + * @brief Returns true if a container is also key-only, false otherwise. + * @return True if the associative container is also key-only, false otherwise. + */ +[[nodiscard]] inline bool meta_associative_container::key_only() const ENTT_NOEXCEPT { +return key_only_container; +} + +/** + * @brief Returns the meta key type of a container. + * @return The meta key type of the a container. + */ +[[nodiscard]] inline meta_type meta_associative_container::key_type() const ENTT_NOEXCEPT { +return key_type_node; +} + +/** + * @brief Returns the meta mapped type of a container. + * @return The meta mapped type of the a container. + */ +[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const ENTT_NOEXCEPT { +return mapped_type_node; +} + +/*! @copydoc meta_sequence_container::value_type */ +[[nodiscard]] inline meta_type meta_associative_container::value_type() const ENTT_NOEXCEPT { +return value_type_node; +} + +/*! @copydoc meta_sequence_container::size */ +[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const ENTT_NOEXCEPT { +return size_fn(storage); +} + +/*! @copydoc meta_sequence_container::clear */ +inline bool meta_associative_container::clear() { +return clear_fn(storage); +} + +/*! @copydoc meta_sequence_container::begin */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { +return iter_fn(storage, false); +} + +/*! @copydoc meta_sequence_container::end */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { +return iter_fn(storage, true); +} + +/** + * @brief Inserts an element (a key/value pair) into a container. + * @param key The key of the element to insert. + * @param value The value of the element to insert. + * @return A bool denoting whether the insertion took place. + */ +inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) { +return insert_fn(storage, key, value); +} + +/** + * @brief Removes the specified element from a container. + * @param key The key of the element to remove. + * @return A bool denoting whether the removal took place. + */ +inline bool meta_associative_container::erase(meta_any key) { +return erase_fn(storage, key); +} + +/** + * @brief Returns an iterator to the element with a given key, if any. + * @param key The key of the element to search. + * @return An iterator to the element with the given key, if any. + */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) { +return find_fn(storage, key); +} + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_associative_container::operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(storage); +} + +} // namespace entt + +#endif + +// #include "meta/node.hpp" +#ifndef ENTT_META_NODE_HPP +#define ENTT_META_NODE_HPP + +#include <cstddef> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "../core/enum.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "type_traits.hpp" + + +namespace entt { + +class meta_any; +class meta_type; +struct meta_handle; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +enum class meta_traits : std::uint32_t { +is_none = 0x0000, +is_const = 0x0001, +is_static = 0x0002, +is_arithmetic = 0x0004, +is_array = 0x0008, +is_enum = 0x0010, +is_class = 0x0020, +is_pointer = 0x0040, +is_meta_pointer_like = 0x0080, +is_meta_sequence_container = 0x0100, +is_meta_associative_container = 0x0200, +_entt_enum_as_bitmask +}; + +struct meta_type_node; + +struct meta_prop_node { +meta_prop_node *next; +const meta_any &id; +meta_any &value; +}; + +struct meta_base_node { +meta_base_node *next; +meta_type_node *const type; +meta_any (*const cast)(meta_any) ENTT_NOEXCEPT; +}; + +struct meta_conv_node { +meta_conv_node *next; +meta_type_node *const type; +meta_any (*const conv)(const meta_any &); +}; + +struct meta_ctor_node { +using size_type = std::size_t; +meta_ctor_node *next; +const size_type arity; +meta_type (*const arg)(const size_type) ENTT_NOEXCEPT; +meta_any (*const invoke)(meta_any *const); +}; + +struct meta_data_node { +using size_type = std::size_t; +id_type id; +const meta_traits traits; +meta_data_node *next; +meta_prop_node *prop; +const size_type arity; +meta_type_node *const type; +meta_type (*const arg)(const size_type) ENTT_NOEXCEPT; +bool (*const set)(meta_handle, meta_any); +meta_any (*const get)(meta_handle); +}; + +struct meta_func_node { +using size_type = std::size_t; +id_type id; +const meta_traits traits; +meta_func_node *next; +meta_prop_node *prop; +const size_type arity; +meta_type_node *const ret; +meta_type (*const arg)(const size_type) ENTT_NOEXCEPT; +meta_any (*const invoke)(meta_handle, meta_any *const); +}; + +struct meta_template_node { +using size_type = std::size_t; +const size_type arity; +meta_type_node *const type; +meta_type_node *(*const arg)(const size_type)ENTT_NOEXCEPT; +}; + +struct meta_type_node { +using size_type = std::size_t; +const type_info *info; +id_type id; +const meta_traits traits; +meta_type_node *next; +meta_prop_node *prop; +const size_type size_of; +meta_any (*const default_constructor)(); +double (*const conversion_helper)(void *, const void *); +const meta_template_node *const templ; +meta_ctor_node *ctor{nullptr}; +meta_base_node *base{nullptr}; +meta_conv_node *conv{nullptr}; +meta_data_node *data{nullptr}; +meta_func_node *func{nullptr}; +void (*dtor)(void *){nullptr}; +}; + +template<typename... Args> +meta_type_node *meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT; + +template<typename Type> +class ENTT_API meta_node { +static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Invalid type"); + +[[nodiscard]] static auto *meta_default_constructor() ENTT_NOEXCEPT { +if constexpr(std::is_default_constructible_v<Type>) { +return +[]() { return meta_any{std::in_place_type<Type>}; }; +} else { +return static_cast<std::decay_t<decltype(meta_type_node::default_constructor)>>(nullptr); +} +} + +[[nodiscard]] static auto *meta_conversion_helper() ENTT_NOEXCEPT { +if constexpr(std::is_arithmetic_v<Type>) { +return +[](void *bin, const void *value) { +return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(*static_cast<const double *>(value))) : static_cast<double>(*static_cast<const Type *>(value)); +}; +} else if constexpr(std::is_enum_v<Type>) { +return +[](void *bin, const void *value) { +return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(static_cast<std::underlying_type_t<Type>>(*static_cast<const double *>(value)))) : static_cast<double>(*static_cast<const Type *>(value)); +}; +} else { +return static_cast<std::decay_t<decltype(meta_type_node::conversion_helper)>>(nullptr); +} +} + +[[nodiscard]] static meta_template_node *meta_template_info() ENTT_NOEXCEPT { +if constexpr(is_complete_v<meta_template_traits<Type>>) { +static meta_template_node node{ +meta_template_traits<Type>::args_type::size, +meta_node<typename meta_template_traits<Type>::class_type>::resolve(), +[](const std::size_t index) ENTT_NOEXCEPT { return meta_arg_node(typename meta_template_traits<Type>::args_type{}, index); } +// tricks clang-format +}; + +return &node; +} else { +return nullptr; +} +} + +public: +[[nodiscard]] static meta_type_node *resolve() ENTT_NOEXCEPT { +static meta_type_node node{ +&type_id<Type>(), +{}, +internal::meta_traits::is_none +| (std::is_arithmetic_v<Type> ? internal::meta_traits::is_arithmetic : internal::meta_traits::is_none) +| (std::is_array_v<Type> ? internal::meta_traits::is_array : internal::meta_traits::is_none) +| (std::is_enum_v<Type> ? internal::meta_traits::is_enum : internal::meta_traits::is_none) +| (std::is_class_v<Type> ? internal::meta_traits::is_class : internal::meta_traits::is_none) +| (std::is_pointer_v<Type> ? internal::meta_traits::is_pointer : internal::meta_traits::is_none) +| (is_meta_pointer_like_v<Type> ? internal::meta_traits::is_meta_pointer_like : internal::meta_traits::is_none) +| (is_complete_v<meta_sequence_container_traits<Type>> ? internal::meta_traits::is_meta_sequence_container : internal::meta_traits::is_none) +| (is_complete_v<meta_associative_container_traits<Type>> ? internal::meta_traits::is_meta_associative_container : internal::meta_traits::is_none), +nullptr, +nullptr, +size_of_v<Type>, +meta_default_constructor(), +meta_conversion_helper(), +meta_template_info() +// tricks clang-format +}; + +return &node; +} +}; + +template<typename... Args> +[[nodiscard]] meta_type_node *meta_arg_node(type_list<Args...>, const std::size_t index) ENTT_NOEXCEPT { +meta_type_node *args[sizeof...(Args) + 1u]{nullptr, internal::meta_node<std::remove_cv_t<std::remove_reference_t<Args>>>::resolve()...}; +return args[index + 1u]; +} + +template<auto Member, typename Type> +[[nodiscard]] static std::decay_t<decltype(std::declval<internal::meta_type_node>().*Member)> find_by(const Type &info_or_id, const internal::meta_type_node *node) ENTT_NOEXCEPT { +for(auto *curr = node->*Member; curr; curr = curr->next) { +if constexpr(std::is_same_v<Type, type_info>) { +if(*curr->type->info == info_or_id) { +return curr; +} +} else if constexpr(std::is_same_v<decltype(curr), meta_base_node *>) { +if(curr->type->id == info_or_id) { +return curr; +} +} else { +if(curr->id == info_or_id) { +return curr; +} +} +} + +for(auto *curr = node->base; curr; curr = curr->next) { +if(auto *ret = find_by<Member>(info_or_id, curr->type); ret) { +return ret; +} +} + +return nullptr; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +} // namespace entt + +#endif + +// #include "meta/pointer.hpp" +#ifndef ENTT_META_POINTER_HPP +#define ENTT_META_POINTER_HPP + +#include <memory> +#include <type_traits> +// #include "type_traits.hpp" + + +namespace entt { + +/** + * @brief Makes plain pointers pointer-like types for the meta system. + * @tparam Type Element type. + */ +template<typename Type> +struct is_meta_pointer_like<Type *> +: std::true_type {}; + +/** + * @brief Partial specialization used to reject pointers to arrays. + * @tparam Type Type of elements of the array. + * @tparam N Number of elements of the array. + */ +template<typename Type, std::size_t N> +struct is_meta_pointer_like<Type (*)[N]> +: std::false_type {}; + +/** + * @brief Makes `std::shared_ptr`s of any type pointer-like types for the meta + * system. + * @tparam Type Element type. + */ +template<typename Type> +struct is_meta_pointer_like<std::shared_ptr<Type>> +: std::true_type {}; + +/** + * @brief Makes `std::unique_ptr`s of any type pointer-like types for the meta + * system. + * @tparam Type Element type. + * @tparam Args Other arguments. + */ +template<typename Type, typename... Args> +struct is_meta_pointer_like<std::unique_ptr<Type, Args...>> +: std::true_type {}; + +} // namespace entt + +#endif + +// #include "meta/policy.hpp" +#ifndef ENTT_META_POLICY_HPP +#define ENTT_META_POLICY_HPP + +#include <type_traits> + +namespace entt { + +/*! @brief Empty class type used to request the _as ref_ policy. */ +struct as_ref_t { +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ +template<typename Type> +static constexpr bool value = std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>; +/** + * Internal details not to be documented. + * @endcond + */ +}; + +/*! @brief Empty class type used to request the _as cref_ policy. */ +struct as_cref_t { +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ +template<typename Type> +static constexpr bool value = std::is_reference_v<Type>; +/** + * Internal details not to be documented. + * @endcond + */ +}; + +/*! @brief Empty class type used to request the _as-is_ policy. */ +struct as_is_t { +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ +template<typename> +static constexpr bool value = true; +/** + * Internal details not to be documented. + * @endcond + */ +}; + +/*! @brief Empty class type used to request the _as void_ policy. */ +struct as_void_t { +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ +template<typename> +static constexpr bool value = true; +/** + * Internal details not to be documented. + * @endcond + */ +}; + +} // namespace entt + +#endif + +// #include "meta/range.hpp" +#ifndef ENTT_META_RANGE_HPP +#define ENTT_META_RANGE_HPP + +#include <cstddef> +#include <iterator> +// #include "../core/iterator.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type, typename Node> +struct meta_range_iterator final { +using difference_type = std::ptrdiff_t; +using value_type = Type; +using pointer = input_iterator_pointer<value_type>; +using reference = value_type; +using iterator_category = std::input_iterator_tag; +using node_type = Node; + +meta_range_iterator() ENTT_NOEXCEPT +: it{} {} + +meta_range_iterator(node_type *head) ENTT_NOEXCEPT +: it{head} {} + +meta_range_iterator &operator++() ENTT_NOEXCEPT { +return (it = it->next), *this; +} + +meta_range_iterator operator++(int) ENTT_NOEXCEPT { +meta_range_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return it; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +[[nodiscard]] bool operator==(const meta_range_iterator &other) const ENTT_NOEXCEPT { +return it == other.it; +} + +[[nodiscard]] bool operator!=(const meta_range_iterator &other) const ENTT_NOEXCEPT { +return !(*this == other); +} + +private: +node_type *it; +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Iterable range to use to iterate all types of meta objects. + * @tparam Type Type of meta objects returned. + * @tparam Node Type of meta nodes iterated. + */ +template<typename Type, typename Node = typename Type::node_type> +struct meta_range final { +/*! @brief Node type. */ +using node_type = Node; +/*! @brief Input iterator type. */ +using iterator = internal::meta_range_iterator<Type, Node>; +/*! @brief Constant input iterator type. */ +using const_iterator = iterator; + +/*! @brief Default constructor. */ +meta_range() ENTT_NOEXCEPT = default; + +/** + * @brief Constructs a meta range from a given node. + * @param head The underlying node with which to construct the range. + */ +meta_range(node_type *head) ENTT_NOEXCEPT +: node{head} {} + +/** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first meta object of the range. + */ +[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { +return iterator{node}; +} + +/*! @copydoc cbegin */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return cbegin(); +} + +/** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last meta object of the + * range. + */ +[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { +return iterator{}; +} + +/*! @copydoc cend */ +[[nodiscard]] iterator end() const ENTT_NOEXCEPT { +return cend(); +} + +private: +node_type *node{nullptr}; +}; + +} // namespace entt + +#endif + +// #include "meta/resolve.hpp" +#ifndef ENTT_META_RESOLVE_HPP +#define ENTT_META_RESOLVE_HPP + +#include <algorithm> +// #include "../core/type_info.hpp" + +// #include "ctx.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "range.hpp" + + +namespace entt { + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @return The meta type associated with the given type, if any. + */ +template<typename Type> +[[nodiscard]] meta_type resolve() ENTT_NOEXCEPT { +return internal::meta_node<std::remove_cv_t<std::remove_reference_t<Type>>>::resolve(); +} + +/** + * @brief Returns a range to use to visit all meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range<meta_type> resolve() ENTT_NOEXCEPT { +return *internal::meta_context::global(); +} + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve(const id_type id) ENTT_NOEXCEPT { +for(auto &&curr: resolve()) { +if(curr.id() == id) { +return curr; +} +} + +return {}; +} + +/** + * @brief Returns the meta type associated with a given type info object. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. + */ +[[nodiscard]] inline meta_type resolve(const type_info &info) ENTT_NOEXCEPT { +for(auto &&curr: resolve()) { +if(curr.info() == info) { +return curr; +} +} + +return {}; +} + +} // namespace entt + +#endif + +// #include "meta/template.hpp" +#ifndef ENTT_META_TEMPLATE_HPP +#define ENTT_META_TEMPLATE_HPP + +// #include "../core/type_traits.hpp" + + +namespace entt { + +/*! @brief Utility class to disambiguate class templates. */ +template<template<typename...> class> +struct meta_class_template_tag {}; + +/** + * @brief General purpose traits class for generating meta template information. + * @tparam Clazz Type of class template. + * @tparam Args Types of template arguments. + */ +template<template<typename...> class Clazz, typename... Args> +struct meta_template_traits<Clazz<Args...>> { +/*! @brief Wrapped class template. */ +using class_type = meta_class_template_tag<Clazz>; +/*! @brief List of template arguments. */ +using args_type = type_list<Args...>; +}; + +} // namespace entt + +#endif + +// #include "meta/type_traits.hpp" +#ifndef ENTT_META_TYPE_TRAITS_HPP +#define ENTT_META_TYPE_TRAITS_HPP + +#include <type_traits> +#include <utility> + +namespace entt { + +/** + * @brief Traits class template to be specialized to enable support for meta + * template information. + */ +template<typename> +struct meta_template_traits; + +/** + * @brief Traits class template to be specialized to enable support for meta + * sequence containers. + */ +template<typename> +struct meta_sequence_container_traits; + +/** + * @brief Traits class template to be specialized to enable support for meta + * associative containers. + */ +template<typename> +struct meta_associative_container_traits; + +/** + * @brief Provides the member constant `value` to true if a given type is a + * pointer-like type from the point of view of the meta system, false otherwise. + * @tparam Type Potentially pointer-like type. + */ +template<typename> +struct is_meta_pointer_like: std::false_type {}; + +/** + * @brief Partial specialization to ensure that const pointer-like types are + * also accepted. + * @tparam Type Potentially pointer-like type. + */ +template<typename Type> +struct is_meta_pointer_like<const Type>: is_meta_pointer_like<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type Potentially pointer-like type. + */ +template<typename Type> +inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like<Type>::value; + +} // namespace entt + +#endif + +// #include "meta/utility.hpp" +#ifndef ENTT_META_UTILITY_HPP +#define ENTT_META_UTILITY_HPP + +#include <cstddef> +#include <functional> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "policy.hpp" + + +namespace entt { + +/*! @brief Primary template isn't defined on purpose. */ +template<typename, typename> +struct meta_function_descriptor; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template<typename Type, typename Ret, typename Class, typename... Args> +struct meta_function_descriptor<Type, Ret (Class::*)(Args...) const> { +/*! @brief Meta function return type. */ +using return_type = Ret; +/*! @brief Meta function arguments. */ +using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<Args...>, type_list<const Class &, Args...>>; + +/*! @brief True if the meta function is const, false otherwise. */ +static constexpr auto is_const = true; +/*! @brief True if the meta function is static, false otherwise. */ +static constexpr auto is_static = !std::is_base_of_v<Class, Type>; +}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template<typename Type, typename Ret, typename Class, typename... Args> +struct meta_function_descriptor<Type, Ret (Class::*)(Args...)> { +/*! @brief Meta function return type. */ +using return_type = Ret; +/*! @brief Meta function arguments. */ +using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<Args...>, type_list<Class &, Args...>>; + +/*! @brief True if the meta function is const, false otherwise. */ +static constexpr auto is_const = false; +/*! @brief True if the meta function is static, false otherwise. */ +static constexpr auto is_static = !std::is_base_of_v<Class, Type>; +}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta data is associated. + * @tparam Class Actual owner of the data member. + * @tparam Ret Data member type. + */ +template<typename Type, typename Ret, typename Class> +struct meta_function_descriptor<Type, Ret Class::*> { +/*! @brief Meta data return type. */ +using return_type = Ret &; +/*! @brief Meta data arguments. */ +using args_type = std::conditional_t<std::is_base_of_v<Class, Type>, type_list<>, type_list<Class &>>; + +/*! @brief True if the meta data is const, false otherwise. */ +static constexpr auto is_const = false; +/*! @brief True if the meta data is static, false otherwise. */ +static constexpr auto is_static = !std::is_base_of_v<Class, Type>; +}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam MaybeType First function argument. + * @tparam Args Other function arguments. + */ +template<typename Type, typename Ret, typename MaybeType, typename... Args> +struct meta_function_descriptor<Type, Ret (*)(MaybeType, Args...)> { +/*! @brief Meta function return type. */ +using return_type = Ret; +/*! @brief Meta function arguments. */ +using args_type = 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...>>; + +/*! @brief True if the meta function is const, false otherwise. */ +static constexpr auto is_const = std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> && std::is_const_v<std::remove_reference_t<MaybeType>>; +/*! @brief True if the meta function is static, false otherwise. */ +static constexpr auto is_static = !std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>; +}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + */ +template<typename Type, typename Ret> +struct meta_function_descriptor<Type, Ret (*)()> { +/*! @brief Meta function return type. */ +using return_type = Ret; +/*! @brief Meta function arguments. */ +using args_type = type_list<>; + +/*! @brief True if the meta function is const, false otherwise. */ +static constexpr auto is_const = false; +/*! @brief True if the meta function is static, false otherwise. */ +static constexpr auto is_static = true; +}; + +/** + * @brief Meta function helper. + * + * Converts a function type to be associated with a reflected type into its meta + * function descriptor. + * + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Candidate The actual function to associate with the reflected type. + */ +template<typename Type, typename Candidate> +class meta_function_helper { +template<typename Ret, typename... Args, typename Class> +static constexpr meta_function_descriptor<Type, Ret (Class::*)(Args...) const> get_rid_of_noexcept(Ret (Class::*)(Args...) const); + +template<typename Ret, typename... Args, typename Class> +static constexpr meta_function_descriptor<Type, Ret (Class::*)(Args...)> get_rid_of_noexcept(Ret (Class::*)(Args...)); + +template<typename Ret, typename Class> +static constexpr meta_function_descriptor<Type, Ret Class::*> get_rid_of_noexcept(Ret Class::*); + +template<typename Ret, typename... Args> +static constexpr meta_function_descriptor<Type, Ret (*)(Args...)> get_rid_of_noexcept(Ret (*)(Args...)); + +template<typename Class> +static constexpr meta_function_descriptor<Class, decltype(&Class::operator())> get_rid_of_noexcept(Class); + +public: +/*! @brief The meta function descriptor of the given function. */ +using type = decltype(get_rid_of_noexcept(std::declval<Candidate>())); +}; + +/** + * @brief Helper type. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Candidate The actual function to associate with the reflected type. + */ +template<typename Type, typename Candidate> +using meta_function_helper_t = typename meta_function_helper<Type, Candidate>::type; + +/** + * @brief Wraps a value depending on the given policy. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Type Type of value to wrap. + * @param value Value to wrap. + * @return A meta any containing the returned value, if any. + */ +template<typename Policy = as_is_t, typename Type> +meta_any meta_dispatch([[maybe_unused]] Type &&value) { +if constexpr(std::is_same_v<Policy, as_void_t>) { +return meta_any{std::in_place_type<void>}; +} else if constexpr(std::is_same_v<Policy, as_ref_t>) { +return meta_any{std::in_place_type<Type>, std::forward<Type>(value)}; +} else if constexpr(std::is_same_v<Policy, as_cref_t>) { +static_assert(std::is_lvalue_reference_v<Type>, "Invalid type"); +return meta_any{std::in_place_type<const std::remove_reference_t<Type> &>, std::as_const(value)}; +} else { +static_assert(std::is_same_v<Policy, as_is_t>, "Policy not supported"); +return meta_any{std::forward<Type>(value)}; +} +} + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @return The meta type of the i-th element of the list of arguments. + */ +template<typename Type> +[[nodiscard]] static meta_type meta_arg(const std::size_t index) ENTT_NOEXCEPT { +return internal::meta_arg_node(Type{}, index); +} + +/** + * @brief Sets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to set. + * @param instance An opaque instance of the underlying type, if required. + * @param value Parameter to use to set the variable. + * @return True in case of success, false otherwise. + */ +template<typename Type, auto Data> +[[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { +if constexpr(!std::is_same_v<decltype(Data), Type> && !std::is_same_v<decltype(Data), std::nullptr_t>) { +if constexpr(std::is_member_function_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) { +using descriptor = meta_function_helper_t<Type, decltype(Data)>; +using data_type = type_list_element_t<descriptor::is_static, typename descriptor::args_type>; + +if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) { +std::invoke(Data, *clazz, value.cast<data_type>()); +return true; +} +} else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) { +using data_type = std::remove_reference_t<typename meta_function_helper_t<Type, decltype(Data)>::return_type>; + +if constexpr(!std::is_array_v<data_type> && !std::is_const_v<data_type>) { +if(auto *const clazz = instance->try_cast<Type>(); clazz && value.allow_cast<data_type>()) { +std::invoke(Data, *clazz) = value.cast<data_type>(); +return true; +} +} +} else { +using data_type = std::remove_reference_t<decltype(*Data)>; + +if constexpr(!std::is_array_v<data_type> && !std::is_const_v<data_type>) { +if(value.allow_cast<data_type>()) { +*Data = value.cast<data_type>(); +return true; +} +} +} +} + +return false; +} + +/** + * @brief Gets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to get. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @return A meta any containing the value of the underlying variable. + */ +template<typename Type, auto Data, typename Policy = as_is_t> +[[nodiscard]] meta_any meta_getter([[maybe_unused]] meta_handle instance) { +if constexpr(std::is_member_pointer_v<decltype(Data)> || std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>>) { +if constexpr(!std::is_array_v<std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>>>) { +if constexpr(std::is_invocable_v<decltype(Data), Type &>) { +if(auto *clazz = instance->try_cast<Type>(); clazz) { +return meta_dispatch<Policy>(std::invoke(Data, *clazz)); +} +} + +if constexpr(std::is_invocable_v<decltype(Data), const Type &>) { +if(auto *fallback = instance->try_cast<const Type>(); fallback) { +return meta_dispatch<Policy>(std::invoke(Data, *fallback)); +} +} +} + +return meta_any{}; +} else if constexpr(std::is_pointer_v<decltype(Data)>) { +if constexpr(std::is_array_v<std::remove_pointer_t<decltype(Data)>>) { +return meta_any{}; +} else { +return meta_dispatch<Policy>(*Data); +} +} else { +return meta_dispatch<Policy>(Data); +} +} + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type, typename Policy, typename Candidate, typename... Args> +[[nodiscard]] meta_any meta_invoke_with_args(Candidate &&candidate, Args &&...args) { +if constexpr(std::is_same_v<std::invoke_result_t<decltype(candidate), Args...>, void>) { +std::invoke(candidate, args...); +return meta_any{std::in_place_type<void>}; +} else { +return meta_dispatch<Policy>(std::invoke(candidate, args...)); +} +} + +template<typename Type, typename Policy, typename Candidate, std::size_t... Index> +[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence<Index...>) { +using descriptor = meta_function_helper_t<Type, std::remove_reference_t<Candidate>>; + +if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, const Type &, type_list_element_t<Index, typename descriptor::args_type>...>) { +if(const auto *const clazz = instance->try_cast<const Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) { +return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...); +} +} else if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, Type &, type_list_element_t<Index, typename descriptor::args_type>...>) { +if(auto *const clazz = instance->try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) { +return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...); +} +} else { +if(((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) { +return meta_invoke_with_args<Type, Policy>(std::forward<Candidate>(candidate), (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...); +} +} + +return meta_any{}; +} + +template<typename Type, typename... Args, std::size_t... Index> +[[nodiscard]] meta_any meta_construct(meta_any *const args, std::index_sequence<Index...>) { +if(((args + Index)->allow_cast<Args>() && ...)) { +return meta_any{std::in_place_type<Type>, (args + Index)->cast<Args>()...}; +} + +return meta_any{}; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Tries to _invoke_ an object given a list of erased parameters. + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param instance An opaque instance of the underlying type, if required. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template<typename Type, typename Policy = as_is_t, typename Candidate> +[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args) { +return internal::meta_invoke<Type, Policy>(std::move(instance), std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{}); +} + +/** + * @brief Tries to invoke a function given a list of erased parameters. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template<typename Type, auto Candidate, typename Policy = as_is_t> +[[nodiscard]] meta_any meta_invoke(meta_handle instance, meta_any *const args) { +return internal::meta_invoke<Type, Policy>(std::move(instance), Candidate, args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<decltype(Candidate)>>::args_type::size>{}); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template<typename Type, typename... Args> +[[nodiscard]] meta_any meta_construct(meta_any *const args) { +return internal::meta_construct<Type, Args...>(args, std::index_sequence_for<Args...>{}); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @param candidate The actual object to _invoke_. + * @return A meta any containing the returned value, if any. + */ +template<typename Type, typename Policy = as_is_t, typename Candidate> +[[nodiscard]] meta_any meta_construct(Candidate &&candidate, meta_any *const args) { +if constexpr(meta_function_helper_t<Type, Candidate>::is_static) { +return internal::meta_invoke<Type, Policy>({}, std::forward<Candidate>(candidate), args, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{}); +} else { +return internal::meta_invoke<Type, Policy>(*args, std::forward<Candidate>(candidate), args + 1u, std::make_index_sequence<meta_function_helper_t<Type, std::remove_reference_t<Candidate>>::args_type::size>{}); +} +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template<typename Type, auto Candidate, typename Policy = as_is_t> +[[nodiscard]] meta_any meta_construct(meta_any *const args) { +return meta_construct<Type, Policy>(Candidate, args); +} + +} // namespace entt + +#endif + +// #include "platform/android-ndk-r17.hpp" +#ifndef ENTT_PLATFORM_ANDROID_NDK_R17_HPP +#define ENTT_PLATFORM_ANDROID_NDK_R17_HPP + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +#ifdef __ANDROID__ +# include <android/ndk-version.h> +# if __NDK_MAJOR__ == 17 + +# include <functional> +# include <type_traits> +# include <utility> + +namespace std { + +namespace internal { + +template<typename Func, typename... Args> +constexpr auto is_invocable(int) -> decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...), std::true_type{}); + +template<typename, typename...> +constexpr std::false_type is_invocable(...); + +template<typename Ret, typename Func, typename... Args> +constexpr auto is_invocable_r(int) +-> std::enable_if_t<decltype(std::is_convertible_v<decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...)), Ret>, std::true_type>; + + +template<typename, typename, typename...> +constexpr std::false_type is_invocable_r(...); + +} // namespace internal + +template<typename Func, typename... Args> +struct is_invocable: decltype(internal::is_invocable<Func, Args...>(0)) {}; + +template<typename Func, typename... Argsv> +inline constexpr bool is_invocable_v = std::is_invocable<Func, Args...>::value; + +template<typename Ret, typename Func, typename... Args> +struct is_invocable_r: decltype(internal::is_invocable_r<Ret, Func, Args...>(0)) {}; + +template<typename Ret, typename Func, typename... Args> +inline constexpr bool is_invocable_r_v = std::is_invocable_r<Ret, Func, Args...>::value; + +template<typename Func, typename... Args> +struct invoke_result { +using type = decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...)); +}; + +template<typename Func, typename... Args> +using invoke_result_t = typename std::invoke_result<Func, Args...>::type; + +} // namespace std + +# endif +#endif + +/** + * Internal details not to be documented. + * @endcond + */ + +#endif + +// #include "poly/poly.hpp" +#ifndef ENTT_POLY_POLY_HPP +#define ENTT_POLY_POLY_HPP + +#include <cstddef> +#include <functional> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + +#include <cstddef> +#include <memory> +#include <type_traits> +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { +/*! @brief Indicates that this is a transparent function object. */ +using is_transparent = void; + +/** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ +template<class Type> +[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { +return std::forward<Type>(value); +} +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template<typename Type, typename Class> +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template<typename Func> +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template<class... Func> +struct overloaded: Func... { +using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template<class... Func> +overloaded(Func...) -> overloaded<Func...>; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template<class Func> +struct y_combinator { +/** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ +y_combinator(Func recursive) +: func{std::move(recursive)} {} + +/** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ +template<class... Args> +decltype(auto) operator()(Args &&...args) const { +return func(*this, std::forward<Args>(args)...); +} + +/*! @copydoc operator()() */ +template<class... Args> +decltype(auto) operator()(Args &&...args) { +return func(*this, std::forward<Args>(args)...); +} + +private: +Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include <cstdint> +#include <type_traits> +// #include "../config/config.h" + + +namespace entt { + +template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)> +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include <string_view> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#endif + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include <cstddef> +#include <cstdint> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename> +struct fnv1a_traits; + +template<> +struct fnv1a_traits<std::uint32_t> { +using type = std::uint32_t; +static constexpr std::uint32_t offset = 2166136261; +static constexpr std::uint32_t prime = 16777619; +}; + +template<> +struct fnv1a_traits<std::uint64_t> { +using type = std::uint64_t; +static constexpr std::uint64_t offset = 14695981039346656037ull; +static constexpr std::uint64_t prime = 1099511628211ull; +}; + +template<typename Char> +struct basic_hashed_string { +using value_type = Char; +using size_type = std::size_t; +using hash_type = id_type; + +const value_type *repr; +size_type length; +hash_type hash; +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers in the codebase while using their numeric + * counterparts at runtime.<br/> + * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +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>; + +struct const_wrapper { +// non-explicit constructor on purpose +constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {} +const Char *repr; +}; + +// Fowler–Noll–Vo hash function v. 1a - the good +[[nodiscard]] static constexpr auto helper(const Char *str) ENTT_NOEXCEPT { +base_type base{str, 0u, hs_traits::offset}; + +for(; str[base.length]; ++base.length) { +base.hash = (base.hash ^ static_cast<hs_traits::type>(str[base.length])) * hs_traits::prime; +} + +return base; +} + +// Fowler–Noll–Vo hash function v. 1a - the good +[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) ENTT_NOEXCEPT { +base_type base{str, len, hs_traits::offset}; + +for(size_type pos{}; pos < len; ++pos) { +base.hash = (base.hash ^ static_cast<hs_traits::type>(str[pos])) * hs_traits::prime; +} + +return base; +} + +public: +/*! @brief Character type. */ +using value_type = typename base_type::value_type; +/*! @brief Unsigned integer type. */ +using size_type = typename base_type::size_type; +/*! @brief Unsigned integer type. */ +using hash_type = typename base_type::hash_type; + +/** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ +[[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) ENTT_NOEXCEPT { +return basic_hashed_string{str, len}; +} + +/** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ +template<std::size_t N> +[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { +return basic_hashed_string{str}; +} + +/** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ +[[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { +return basic_hashed_string{wrapper}; +} + +/*! @brief Constructs an empty hashed string. */ +constexpr basic_hashed_string() ENTT_NOEXCEPT +: base_type{} {} + +/** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +constexpr basic_hashed_string(const value_type *str, const size_type len) ENTT_NOEXCEPT +: base_type{helper(str, len)} {} + +/** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template<std::size_t N> +constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT +: base_type{helper(str)} {} + +/** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ +explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT +: base_type{helper(wrapper.repr)} {} + +/** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ +[[nodiscard]] constexpr size_type size() const ENTT_NOEXCEPT { +return base_type::length; +} + +/** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ +[[nodiscard]] constexpr const value_type *data() const ENTT_NOEXCEPT { +return base_type::repr; +} + +/** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ +[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { +return base_type::hash; +} + +/*! @copydoc data */ +[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { +return data(); +} + +/** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ +[[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template<typename Char> +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template<typename Char, std::size_t N> +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>; + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string<char>; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string<wchar_t>; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT { +return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { +return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +struct ENTT_API type_index final { +[[nodiscard]] static id_type next() ENTT_NOEXCEPT { +static ENTT_MAYBE_ATOMIC(id_type) value{}; +return value++; +} +}; + +template<typename Type> +[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION +std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; +auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); +auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); +return value; +#else +return std::string_view{""}; +#endif +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { +constexpr auto value = stripped_type_name<Type>(); +return value; +} + +template<typename Type> +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { +static const auto value = stripped_type_name<Type>(); +return value; +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { +constexpr auto stripped = stripped_type_name<Type>(); +constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); +return value; +} + +template<typename Type> +[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { +static const auto value = [](const auto stripped) { +return hashed_string::value(stripped.data(), stripped.size()); +}(stripped_type_name<Type>()); +return value; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template<typename Type, typename = void> +struct ENTT_API type_index final { +/** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ +[[nodiscard]] static id_type value() ENTT_NOEXCEPT { +static const id_type value = internal::type_index::next(); +return value; +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template<typename Type, typename = void> +struct type_hash final { +/** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION +[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { +return internal::type_hash<Type>(0); +#else +[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { +return type_index<Type>::value(); +#endif +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template<typename Type, typename = void> +struct type_name final { +/** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ +[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { +return internal::type_name<Type>(0); +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { +return value(); +} +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { +/** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ +template<typename Type> +constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT +: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, +identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, +alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {} + +/** + * @brief Type index. + * @return Type index. + */ +[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT { +return seq; +} + +/** + * @brief Type hash. + * @return Type hash. + */ +[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { +return identifier; +} + +/** + * @brief Type name. + * @return Type name. + */ +[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT { +return alias; +} + +private: +id_type seq; +id_type identifier; +std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.<br/> + * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template<typename Type> +[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT { +if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) { +static type_info instance{std::in_place_type<Type>}; +return instance; +} else { +return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} +} + +/*! @copydoc type_id */ +template<typename Type> +[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT { +return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template<std::size_t Len, std::size_t Align> +class basic_any { +enum class operation : std::uint8_t { +copy, +move, +transfer, +assign, +destroy, +compare, +get +}; + +enum class policy : std::uint8_t { +owner, +ref, +cref +}; + +using storage_type = std::aligned_storage_t<Len + !Len, Align>; +using vtable_type = const void *(const operation, const basic_any &, const void *); + +template<typename Type> +static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>; + +template<typename Type> +static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] const void *other) { +static_assert(!std::is_same_v<Type, void> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type"); +const Type *element = nullptr; + +if constexpr(in_situ<Type>) { +element = value.owner() ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance); +} else { +element = static_cast<const Type *>(value.instance); +} + +switch(op) { +case operation::copy: +if constexpr(std::is_copy_constructible_v<Type>) { +static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element); +} +break; +case operation::move: +if constexpr(in_situ<Type>) { +if(value.owner()) { +return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))}; +} +} + +return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr)); +case operation::transfer: +if constexpr(std::is_move_assignable_v<Type>) { +*const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other))); +return other; +} +[[fallthrough]]; +case operation::assign: +if constexpr(std::is_copy_assignable_v<Type>) { +*const_cast<Type *>(element) = *static_cast<const Type *>(other); +return other; +} +break; +case operation::destroy: +if constexpr(in_situ<Type>) { +element->~Type(); +} else if constexpr(std::is_array_v<Type>) { +delete[] element; +} else { +delete element; +} +break; +case operation::compare: +if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) { +return *static_cast<const Type *>(element) == *static_cast<const Type *>(other) ? other : nullptr; +} else { +return (element == other) ? other : nullptr; +} +case operation::get: +return element; +} + +return nullptr; +} + +template<typename Type, typename... Args> +void initialize([[maybe_unused]] Args &&...args) { +if constexpr(!std::is_void_v<Type>) { +info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +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"); +mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref; +instance = (std::addressof(args), ...); +} else if constexpr(in_situ<Type>) { +if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) { +new(&storage) Type{std::forward<Args>(args)...}; +} else { +new(&storage) Type(std::forward<Args>(args)...); +} +} else { +if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) { +instance = new Type{std::forward<Args>(args)...}; +} else { +instance = new Type(std::forward<Args>(args)...); +} +} +} +} + +basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT +: instance{other.data()}, +info{other.info}, +vtable{other.vtable}, +mode{pol} {} + +public: +/*! @brief Size of the internal storage. */ +static constexpr auto length = Len; +/*! @brief Alignment requirement. */ +static constexpr auto alignment = Align; + +/*! @brief Default constructor. */ +constexpr basic_any() ENTT_NOEXCEPT +: instance{}, +info{&type_id<void>()}, +vtable{}, +mode{policy::owner} {} + +/** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ +template<typename Type, typename... Args> +explicit basic_any(std::in_place_type_t<Type>, Args &&...args) +: basic_any{} { +initialize<Type>(std::forward<Args>(args)...); +} + +/** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ +template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>> +basic_any(Type &&value) +: basic_any{} { +initialize<std::decay_t<Type>>(std::forward<Type>(value)); +} + +/** + * @brief Copy constructor. + * @param other The instance to copy from. + */ +basic_any(const basic_any &other) +: basic_any{} { +if(other.vtable) { +other.vtable(operation::copy, other, this); +} +} + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +basic_any(basic_any &&other) ENTT_NOEXCEPT +: instance{}, +info{other.info}, +vtable{other.vtable}, +mode{other.mode} { +if(other.vtable) { +other.vtable(operation::move, other, this); +} +} + +/*! @brief Frees the internal storage, whatever it means. */ +~basic_any() { +if(vtable && owner()) { +vtable(operation::destroy, *this, nullptr); +} +} + +/** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ +basic_any &operator=(const basic_any &other) { +reset(); + +if(other.vtable) { +other.vtable(operation::copy, other, this); +} + +return *this; +} + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This any object. + */ +basic_any &operator=(basic_any &&other) ENTT_NOEXCEPT { +reset(); + +if(other.vtable) { +other.vtable(operation::move, other, this); +info = other.info; +vtable = other.vtable; +mode = other.mode; +} + +return *this; +} + +/** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This any object. + */ +template<typename Type> +std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>, basic_any &> +operator=(Type &&value) { +emplace<std::decay_t<Type>>(std::forward<Type>(value)); +return *this; +} + +/** + * @brief Returns the object type if any, `type_id<void>()` otherwise. + * @return The object type if any, `type_id<void>()` otherwise. + */ +[[nodiscard]] const type_info &type() const ENTT_NOEXCEPT { +return *info; +} + +/** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ +[[nodiscard]] const void *data() const ENTT_NOEXCEPT { +return vtable ? vtable(operation::get, *this, nullptr) : nullptr; +} + +/** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ +[[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT { +return *info == req ? data() : nullptr; +} + +/** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ +[[nodiscard]] void *data() ENTT_NOEXCEPT { +return (!vtable || mode == policy::cref) ? nullptr : const_cast<void *>(vtable(operation::get, *this, nullptr)); +} + +/** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ +[[nodiscard]] void *data(const type_info &req) ENTT_NOEXCEPT { +return *info == req ? data() : nullptr; +} + +/** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ +template<typename Type, typename... Args> +void emplace(Args &&...args) { +reset(); +initialize<Type>(std::forward<Args>(args)...); +} + +/** + * @brief Assigns a value to the contained object without replacing it. + * @param other The value to assign to the contained object. + * @return True in case of success, false otherwise. + */ +bool assign(const any &other) { +if(vtable && mode != policy::cref && *info == *other.info) { +return (vtable(operation::assign, *this, other.data()) != nullptr); +} + +return false; +} + +/*! @copydoc assign */ +bool assign(any &&other) { +if(vtable && mode != policy::cref && *info == *other.info) { +if(auto *val = other.data(); val) { +return (vtable(operation::transfer, *this, val) != nullptr); +} else { +return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr); +} +} + +return false; +} + +/*! @brief Destroys contained object */ +void reset() { +if(vtable && owner()) { +vtable(operation::destroy, *this, nullptr); +} + +info = &type_id<void>(); +vtable = nullptr; +mode = policy::owner; +} + +/** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return vtable != nullptr; +} + +/** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ +bool operator==(const basic_any &other) const ENTT_NOEXCEPT { +if(vtable && *info == *other.info) { +return (vtable(operation::compare, *this, other.data()) != nullptr); +} + +return (!vtable && !other.vtable); +} + +/** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ +[[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT { +return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)}; +} + +/*! @copydoc as_ref */ +[[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT { +return basic_any{*this, policy::cref}; +} + +/** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ +[[nodiscard]] bool owner() const ENTT_NOEXCEPT { +return (mode == policy::owner); +} + +private: +union { +const void *instance; +storage_type storage; +}; +const type_info *info; +vtable_type *vtable; +policy mode; +}; + +/** + * @brief Checks if two wrappers differ in their content. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param lhs A wrapper, either empty or not. + * @param rhs A wrapper, either empty or not. + * @return True if the two wrappers differ in their content, false otherwise. + */ +template<std::size_t Len, std::size_t Align> +[[nodiscard]] inline bool operator!=(const basic_any<Len, Align> &lhs, const basic_any<Len, Align> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template<typename Type, std::size_t Len, std::size_t Align> +Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT { +const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); +ENTT_ASSERT(instance, "Invalid instance"); +return static_cast<Type>(*instance); +} + +/*! @copydoc any_cast */ +template<typename Type, std::size_t Len, std::size_t Align> +Type any_cast(basic_any<Len, Align> &data) ENTT_NOEXCEPT { +// forces const on non-reference types to make them work also with wrappers for const references +auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data); +ENTT_ASSERT(instance, "Invalid instance"); +return static_cast<Type>(*instance); +} + +/*! @copydoc any_cast */ +template<typename Type, std::size_t Len, std::size_t Align> +Type any_cast(basic_any<Len, Align> &&data) ENTT_NOEXCEPT { +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)); +} else { +return any_cast<Type>(data); +} +} else { +auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); +ENTT_ASSERT(instance, "Invalid instance"); +return static_cast<Type>(std::move(*instance)); +} +} + +/*! @copydoc any_cast */ +template<typename Type, std::size_t Len, std::size_t Align> +const Type *any_cast(const basic_any<Len, Align> *data) ENTT_NOEXCEPT { +const auto &info = type_id<std::remove_cv_t<std::remove_reference_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) ENTT_NOEXCEPT { +const auto &info = type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +// last attempt to make wrappers for const references return their values +return static_cast<Type *>(static_cast<constness_as_t<basic_any<Len, Align>, Type> *>(data)->data(info)); +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @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) { +return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @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) { +return basic_any<Len, Align>{std::in_place_type<std::conditional_t<std::is_rvalue_reference_v<Type>, std::decay_t<Type>, Type>>, std::forward<Type>(value)}; +} + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include <string_view> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +struct ENTT_API type_index final { +[[nodiscard]] static id_type next() ENTT_NOEXCEPT { +static ENTT_MAYBE_ATOMIC(id_type) value{}; +return value++; +} +}; + +template<typename Type> +[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION +std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; +auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); +auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); +return value; +#else +return std::string_view{""}; +#endif +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { +constexpr auto value = stripped_type_name<Type>(); +return value; +} + +template<typename Type> +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { +static const auto value = stripped_type_name<Type>(); +return value; +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { +constexpr auto stripped = stripped_type_name<Type>(); +constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); +return value; +} + +template<typename Type> +[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { +static const auto value = [](const auto stripped) { +return hashed_string::value(stripped.data(), stripped.size()); +}(stripped_type_name<Type>()); +return value; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template<typename Type, typename = void> +struct ENTT_API type_index final { +/** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ +[[nodiscard]] static id_type value() ENTT_NOEXCEPT { +static const id_type value = internal::type_index::next(); +return value; +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template<typename Type, typename = void> +struct type_hash final { +/** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION +[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { +return internal::type_hash<Type>(0); +#else +[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { +return type_index<Type>::value(); +#endif +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template<typename Type, typename = void> +struct type_name final { +/** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ +[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { +return internal::type_name<Type>(0); +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { +return value(); +} +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { +/** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ +template<typename Type> +constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT +: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, +identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, +alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {} + +/** + * @brief Type index. + * @return Type index. + */ +[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT { +return seq; +} + +/** + * @brief Type hash. + * @return Type hash. + */ +[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { +return identifier; +} + +/** + * @brief Type name. + * @return Type name. + */ +[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT { +return alias; +} + +private: +id_type seq; +id_type identifier; +std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.<br/> + * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template<typename Type> +[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT { +if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) { +static type_info instance{std::in_place_type<Type>}; +return instance; +} else { +return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} +} + +/*! @copydoc type_id */ +template<typename Type> +[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT { +return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_POLY_FWD_HPP +#define ENTT_POLY_FWD_HPP + +#include <cstdint> +#include <type_traits> + +namespace entt { + +template<typename, std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)> +class basic_poly; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Concept Concept descriptor. + */ +template<typename Concept> +using poly = basic_poly<Concept>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @brief Inspector class used to infer the type of the virtual table. */ +struct poly_inspector { +/** + * @brief Generic conversion operator (definition only). + * @tparam Type Type to which conversion is requested. + */ +template<class Type> +operator Type &&() const; + +/** + * @brief Dummy invocation function (definition only). + * @tparam Member Index of the function to invoke. + * @tparam Args Types of arguments to pass to the function. + * @param args The arguments to pass to the function. + * @return A poly inspector convertible to any type. + */ +template<std::size_t Member, typename... Args> +poly_inspector invoke(Args &&...args) const; + +/*! @copydoc invoke */ +template<std::size_t Member, typename... Args> +poly_inspector invoke(Args &&...args); +}; + +/** + * @brief Static virtual table factory. + * @tparam Concept Concept descriptor. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + */ +template<typename Concept, std::size_t Len, std::size_t Align> +class poly_vtable { +using inspector = typename Concept::template type<poly_inspector>; + +template<typename Ret, typename... Args> +static auto vtable_entry(Ret (*)(inspector &, Args...)) -> Ret (*)(basic_any<Len, Align> &, Args...); + +template<typename Ret, typename... Args> +static auto vtable_entry(Ret (*)(const inspector &, Args...)) -> Ret (*)(const basic_any<Len, Align> &, Args...); + +template<typename Ret, typename... Args> +static auto vtable_entry(Ret (*)(Args...)) -> Ret (*)(const basic_any<Len, Align> &, Args...); + +template<typename Ret, typename... Args> +static auto vtable_entry(Ret (inspector::*)(Args...)) -> Ret (*)(basic_any<Len, Align> &, Args...); + +template<typename Ret, typename... Args> +static auto vtable_entry(Ret (inspector::*)(Args...) const) -> Ret (*)(const basic_any<Len, Align> &, Args...); + +template<auto... Candidate> +static auto make_vtable(value_list<Candidate...>) ENTT_NOEXCEPT +-> decltype(std::make_tuple(vtable_entry(Candidate)...)); + +template<typename... Func> +[[nodiscard]] static constexpr auto make_vtable(type_list<Func...>) ENTT_NOEXCEPT { +if constexpr(sizeof...(Func) == 0u) { +return decltype(make_vtable(typename Concept::template impl<inspector>{})){}; +} else if constexpr((std::is_function_v<Func> && ...)) { +return decltype(std::make_tuple(vtable_entry(std::declval<Func inspector::*>())...)){}; +} +} + +template<typename Type, auto Candidate, typename Ret, typename Any, typename... Args> +static void fill_vtable_entry(Ret (*&entry)(Any &, Args...)) ENTT_NOEXCEPT { +if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) { +entry = +[](Any &, Args... args) -> Ret { +return std::invoke(Candidate, std::forward<Args>(args)...); +}; +} else { +entry = +[](Any &instance, Args... args) -> Ret { +return static_cast<Ret>(std::invoke(Candidate, any_cast<constness_as_t<Type, Any> &>(instance), std::forward<Args>(args)...)); +}; +} +} + +template<typename Type, auto... Index> +[[nodiscard]] static auto fill_vtable(std::index_sequence<Index...>) ENTT_NOEXCEPT { +vtable_type impl{}; +(fill_vtable_entry<Type, value_list_element_v<Index, typename Concept::template impl<Type>>>(std::get<Index>(impl)), ...); +return impl; +} + +using vtable_type = decltype(make_vtable(Concept{})); +static constexpr bool is_mono_v = std::tuple_size_v<vtable_type> == 1u; + +public: +/*! @brief Virtual table type. */ +using type = std::conditional_t<is_mono_v, std::tuple_element_t<0u, vtable_type>, const vtable_type *>; + +/** + * @brief Returns a static virtual table for a specific concept and type. + * @tparam Type The type for which to generate the virtual table. + * @return A static virtual table for the given concept and type. + */ +template<typename Type> +[[nodiscard]] static type instance() ENTT_NOEXCEPT { +static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Type differs from its decayed form"); +static const vtable_type vtable = fill_vtable<Type>(std::make_index_sequence<Concept::template impl<Type>::size>{}); + +if constexpr(is_mono_v) { +return std::get<0>(vtable); +} else { +return &vtable; +} +} +}; + +/** + * @brief Poly base class used to inject functionalities into concepts. + * @tparam Poly The outermost poly class. + */ +template<typename Poly> +struct poly_base { +/** + * @brief Invokes a function from the static virtual table. + * @tparam Member Index of the function to invoke. + * @tparam Args Types of arguments to pass to the function. + * @param self A reference to the poly object that made the call. + * @param args The arguments to pass to the function. + * @return The return value of the invoked function, if any. + */ +template<std::size_t Member, typename... Args> +[[nodiscard]] decltype(auto) invoke(const poly_base &self, Args &&...args) const { +const auto &poly = static_cast<const Poly &>(self); + +if constexpr(std::is_function_v<std::remove_pointer_t<decltype(poly.vtable)>>) { +return poly.vtable(poly.storage, std::forward<Args>(args)...); +} else { +return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...); +} +} + +/*! @copydoc invoke */ +template<std::size_t Member, typename... Args> +[[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&...args) { +auto &poly = static_cast<Poly &>(self); + +if constexpr(std::is_function_v<std::remove_pointer_t<decltype(poly.vtable)>>) { +static_assert(Member == 0u, "Unknown member"); +return poly.vtable(poly.storage, std::forward<Args>(args)...); +} else { +return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...); +} +} +}; + +/** + * @brief Shortcut for calling `poly_base<Type>::invoke`. + * @tparam Member Index of the function to invoke. + * @tparam Poly A fully defined poly object. + * @tparam Args Types of arguments to pass to the function. + * @param self A reference to the poly object that made the call. + * @param args The arguments to pass to the function. + * @return The return value of the invoked function, if any. + */ +template<std::size_t Member, typename Poly, typename... Args> +decltype(auto) poly_call(Poly &&self, Args &&...args) { +return std::forward<Poly>(self).template invoke<Member>(self, std::forward<Args>(args)...); +} + +/** + * @brief Static polymorphism made simple and within everyone's reach. + * + * Static polymorphism is a very powerful tool in C++, albeit sometimes + * cumbersome to obtain.<br/> + * This class aims to make it simple and easy to use. + * + * @note + * Both deduced and defined static virtual tables are supported.<br/> + * Moreover, the `poly` class template also works with unmanaged objects. + * + * @tparam Concept Concept descriptor. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template<typename Concept, std::size_t Len, std::size_t Align> +class basic_poly: private Concept::template type<poly_base<basic_poly<Concept, Len, Align>>> { +/*! @brief A poly base is allowed to snoop into a poly object. */ +friend struct poly_base<basic_poly>; + +public: +/*! @brief Concept type. */ +using concept_type = typename Concept::template type<poly_base<basic_poly>>; +/*! @brief Virtual table type. */ +using vtable_type = typename poly_vtable<Concept, Len, Align>::type; + +/*! @brief Default constructor. */ +basic_poly() ENTT_NOEXCEPT +: storage{}, +vtable{} {} + +/** + * @brief Constructs a poly by directly initializing the new object. + * @tparam Type Type of object to use to initialize the poly. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ +template<typename Type, typename... Args> +explicit basic_poly(std::in_place_type_t<Type>, Args &&...args) +: storage{std::in_place_type<Type>, std::forward<Args>(args)...}, +vtable{poly_vtable<Concept, Len, Align>::template instance<std::remove_cv_t<std::remove_reference_t<Type>>>()} {} + +/** + * @brief Constructs a poly from a given value. + * @tparam Type Type of object to use to initialize the poly. + * @param value An instance of an object to use to initialize the poly. + */ +template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, basic_poly>>> +basic_poly(Type &&value) ENTT_NOEXCEPT +: basic_poly{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)} {} + +/** + * @brief Returns the object type if any, `type_id<void>()` otherwise. + * @return The object type if any, `type_id<void>()` otherwise. + */ +[[nodiscard]] const type_info &type() const ENTT_NOEXCEPT { +return storage.type(); +} + +/** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ +[[nodiscard]] const void *data() const ENTT_NOEXCEPT { +return storage.data(); +} + +/*! @copydoc data */ +[[nodiscard]] void *data() ENTT_NOEXCEPT { +return storage.data(); +} + +/** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the poly. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ +template<typename Type, typename... Args> +void emplace(Args &&...args) { +storage.template emplace<Type>(std::forward<Args>(args)...); +vtable = poly_vtable<Concept, Len, Align>::template instance<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} + +/*! @brief Destroys contained object */ +void reset() { +storage.reset(); +vtable = {}; +} + +/** + * @brief Returns false if a poly is empty, true otherwise. + * @return False if the poly is empty, true otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(storage); +} + +/** + * @brief Returns a pointer to the underlying concept. + * @return A pointer to the underlying concept. + */ +[[nodiscard]] concept_type *operator->() ENTT_NOEXCEPT { +return this; +} + +/*! @copydoc operator-> */ +[[nodiscard]] const concept_type *operator->() const ENTT_NOEXCEPT { +return this; +} + +/** + * @brief Aliasing constructor. + * @return A poly that shares a reference to an unmanaged object. + */ +[[nodiscard]] basic_poly as_ref() ENTT_NOEXCEPT { +basic_poly ref{}; +ref.storage = storage.as_ref(); +ref.vtable = vtable; +return ref; +} + +/*! @copydoc as_ref */ +[[nodiscard]] basic_poly as_ref() const ENTT_NOEXCEPT { +basic_poly ref{}; +ref.storage = storage.as_ref(); +ref.vtable = vtable; +return ref; +} + +private: +basic_any<Len, Align> storage; +vtable_type vtable; +}; + +} // namespace entt + +#endif + +// #include "process/process.hpp" +#ifndef ENTT_PROCESS_PROCESS_HPP +#define ENTT_PROCESS_PROCESS_HPP + +#include <cstdint> +#include <type_traits> +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +/** + * @brief Base class for processes. + * + * This class stays true to the CRTP idiom. Derived classes must specify what's + * the intended type for elapsed times.<br/> + * A process should expose publicly the following member functions whether + * required: + * + * * @code{.cpp} + * void update(Delta, void *); + * @endcode + * + * It's invoked once per tick until a process is explicitly aborted or it + * terminates either with or without errors. Even though it's not mandatory to + * declare this member function, as a rule of thumb each process should at + * least define it to work properly. The `void *` parameter is an opaque + * pointer to user data (if any) forwarded directly to the process during an + * update. + * + * * @code{.cpp} + * void init(); + * @endcode + * + * It's invoked when the process joins the running queue of a scheduler. This + * happens as soon as it's attached to the scheduler if the process is a top + * level one, otherwise when it replaces its parent if the process is a + * continuation. + * + * * @code{.cpp} + * void succeeded(); + * @endcode + * + * It's invoked in case of success, immediately after an update and during the + * same tick. + * + * * @code{.cpp} + * void failed(); + * @endcode + * + * It's invoked in case of errors, immediately after an update and during the + * same tick. + * + * * @code{.cpp} + * void aborted(); + * @endcode + * + * It's invoked only if a process is explicitly aborted. There is no guarantee + * that it executes in the same tick, this depends solely on whether the + * process is aborted immediately or not. + * + * Derived classes can change the internal state of a process by invoking the + * `succeed` and `fail` protected member functions and even pause or unpause the + * process itself. + * + * @sa scheduler + * + * @tparam Derived Actual type of process that extends the class template. + * @tparam Delta Type to use to provide elapsed time. + */ +template<typename Derived, typename Delta> +class process { +enum class state : std::uint8_t { +uninitialized = 0, +running, +paused, +succeeded, +failed, +aborted, +finished, +rejected +}; + +template<typename Target = Derived> +auto next(std::integral_constant<state, state::uninitialized>) +-> decltype(std::declval<Target>().init(), void()) { +static_cast<Target *>(this)->init(); +} + +template<typename Target = Derived> +auto next(std::integral_constant<state, state::running>, Delta delta, void *data) +-> decltype(std::declval<Target>().update(delta, data), void()) { +static_cast<Target *>(this)->update(delta, data); +} + +template<typename Target = Derived> +auto next(std::integral_constant<state, state::succeeded>) +-> decltype(std::declval<Target>().succeeded(), void()) { +static_cast<Target *>(this)->succeeded(); +} + +template<typename Target = Derived> +auto next(std::integral_constant<state, state::failed>) +-> decltype(std::declval<Target>().failed(), void()) { +static_cast<Target *>(this)->failed(); +} + +template<typename Target = Derived> +auto next(std::integral_constant<state, state::aborted>) +-> decltype(std::declval<Target>().aborted(), void()) { +static_cast<Target *>(this)->aborted(); +} + +void next(...) const ENTT_NOEXCEPT {} + +protected: +/** + * @brief Terminates a process with success if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ +void succeed() ENTT_NOEXCEPT { +if(alive()) { +current = state::succeeded; +} +} + +/** + * @brief Terminates a process with errors if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ +void fail() ENTT_NOEXCEPT { +if(alive()) { +current = state::failed; +} +} + +/** + * @brief Stops a process if it's in a running state. + * + * The function is idempotent and it does nothing if the process isn't + * running. + */ +void pause() ENTT_NOEXCEPT { +if(current == state::running) { +current = state::paused; +} +} + +/** + * @brief Restarts a process if it's paused. + * + * The function is idempotent and it does nothing if the process isn't + * paused. + */ +void unpause() ENTT_NOEXCEPT { +if(current == state::paused) { +current = state::running; +} +} + +public: +/*! @brief Type used to provide elapsed time. */ +using delta_type = Delta; + +/*! @brief Default destructor. */ +virtual ~process() ENTT_NOEXCEPT { +static_assert(std::is_base_of_v<process, Derived>, "Incorrect use of the class template"); +} + +/** + * @brief Aborts a process if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + * + * @param immediately Requests an immediate operation. + */ +void abort(const bool immediately = false) { +if(alive()) { +current = state::aborted; + +if(immediately) { +tick({}); +} +} +} + +/** + * @brief Returns true if a process is either running or paused. + * @return True if the process is still alive, false otherwise. + */ +[[nodiscard]] bool alive() const ENTT_NOEXCEPT { +return current == state::running || current == state::paused; +} + +/** + * @brief Returns true if a process is already terminated. + * @return True if the process is terminated, false otherwise. + */ +[[nodiscard]] bool finished() const ENTT_NOEXCEPT { +return current == state::finished; +} + +/** + * @brief Returns true if a process is currently paused. + * @return True if the process is paused, false otherwise. + */ +[[nodiscard]] bool paused() const ENTT_NOEXCEPT { +return current == state::paused; +} + +/** + * @brief Returns true if a process terminated with errors. + * @return True if the process terminated with errors, false otherwise. + */ +[[nodiscard]] bool rejected() const ENTT_NOEXCEPT { +return current == state::rejected; +} + +/** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ +void tick(const Delta delta, void *data = nullptr) { +switch(current) { +case state::uninitialized: +next(std::integral_constant<state, state::uninitialized>{}); +current = state::running; +break; +case state::running: +next(std::integral_constant<state, state::running>{}, delta, data); +break; +default: +// suppress warnings +break; +} + +// if it's dead, it must be notified and removed immediately +switch(current) { +case state::succeeded: +next(std::integral_constant<state, state::succeeded>{}); +current = state::finished; +break; +case state::failed: +next(std::integral_constant<state, state::failed>{}); +current = state::rejected; +break; +case state::aborted: +next(std::integral_constant<state, state::aborted>{}); +current = state::rejected; +break; +default: +// suppress warnings +break; +} +} + +private: +state current{state::uninitialized}; +}; + +/** + * @brief Adaptor for lambdas and functors to turn them into processes. + * + * Lambdas and functors can't be used directly with a scheduler for they are not + * properly defined processes with managed life cycles.<br/> + * This class helps in filling the gap and turning lambdas and functors into + * full featured processes usable by a scheduler. + * + * The signature of the function call operator should be equivalent to the + * following: + * + * @code{.cpp} + * void(Delta delta, void *data, auto succeed, auto fail); + * @endcode + * + * Where: + * + * * `delta` is the elapsed time. + * * `data` is an opaque pointer to user data if any, `nullptr` otherwise. + * * `succeed` is a function to call when a process terminates with success. + * * `fail` is a function to call when a process terminates with errors. + * + * The signature of the function call operator of both `succeed` and `fail` + * is equivalent to the following: + * + * @code{.cpp} + * void(); + * @endcode + * + * Usually users shouldn't worry about creating adaptors. A scheduler will + * create them internally each and avery time a lambda or a functor is used as + * a process. + * + * @sa process + * @sa scheduler + * + * @tparam Func Actual type of process. + * @tparam Delta Type to use to provide elapsed time. + */ +template<typename Func, typename Delta> +struct process_adaptor: process<process_adaptor<Func, Delta>, Delta>, private Func { +/** + * @brief Constructs a process adaptor from a lambda or a functor. + * @tparam Args Types of arguments to use to initialize the actual process. + * @param args Parameters to use to initialize the actual process. + */ +template<typename... Args> +process_adaptor(Args &&...args) +: Func{std::forward<Args>(args)...} {} + +/** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ +void update(const Delta delta, void *data) { +Func::operator()( +delta, +data, +[this]() { this->succeed(); }, +[this]() { this->fail(); }); +} +}; + +} // namespace entt + +#endif + +// #include "process/scheduler.hpp" +#ifndef ENTT_PROCESS_SCHEDULER_HPP +#define ENTT_PROCESS_SCHEDULER_HPP + +#include <algorithm> +#include <iterator> +#include <memory> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" + +// #include "process.hpp" +#ifndef ENTT_PROCESS_PROCESS_HPP +#define ENTT_PROCESS_PROCESS_HPP + +#include <cstdint> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Base class for processes. + * + * This class stays true to the CRTP idiom. Derived classes must specify what's + * the intended type for elapsed times.<br/> + * A process should expose publicly the following member functions whether + * required: + * + * * @code{.cpp} + * void update(Delta, void *); + * @endcode + * + * It's invoked once per tick until a process is explicitly aborted or it + * terminates either with or without errors. Even though it's not mandatory to + * declare this member function, as a rule of thumb each process should at + * least define it to work properly. The `void *` parameter is an opaque + * pointer to user data (if any) forwarded directly to the process during an + * update. + * + * * @code{.cpp} + * void init(); + * @endcode + * + * It's invoked when the process joins the running queue of a scheduler. This + * happens as soon as it's attached to the scheduler if the process is a top + * level one, otherwise when it replaces its parent if the process is a + * continuation. + * + * * @code{.cpp} + * void succeeded(); + * @endcode + * + * It's invoked in case of success, immediately after an update and during the + * same tick. + * + * * @code{.cpp} + * void failed(); + * @endcode + * + * It's invoked in case of errors, immediately after an update and during the + * same tick. + * + * * @code{.cpp} + * void aborted(); + * @endcode + * + * It's invoked only if a process is explicitly aborted. There is no guarantee + * that it executes in the same tick, this depends solely on whether the + * process is aborted immediately or not. + * + * Derived classes can change the internal state of a process by invoking the + * `succeed` and `fail` protected member functions and even pause or unpause the + * process itself. + * + * @sa scheduler + * + * @tparam Derived Actual type of process that extends the class template. + * @tparam Delta Type to use to provide elapsed time. + */ +template<typename Derived, typename Delta> +class process { +enum class state : std::uint8_t { +uninitialized = 0, +running, +paused, +succeeded, +failed, +aborted, +finished, +rejected +}; + +template<typename Target = Derived> +auto next(std::integral_constant<state, state::uninitialized>) +-> decltype(std::declval<Target>().init(), void()) { +static_cast<Target *>(this)->init(); +} + +template<typename Target = Derived> +auto next(std::integral_constant<state, state::running>, Delta delta, void *data) +-> decltype(std::declval<Target>().update(delta, data), void()) { +static_cast<Target *>(this)->update(delta, data); +} + +template<typename Target = Derived> +auto next(std::integral_constant<state, state::succeeded>) +-> decltype(std::declval<Target>().succeeded(), void()) { +static_cast<Target *>(this)->succeeded(); +} + +template<typename Target = Derived> +auto next(std::integral_constant<state, state::failed>) +-> decltype(std::declval<Target>().failed(), void()) { +static_cast<Target *>(this)->failed(); +} + +template<typename Target = Derived> +auto next(std::integral_constant<state, state::aborted>) +-> decltype(std::declval<Target>().aborted(), void()) { +static_cast<Target *>(this)->aborted(); +} + +void next(...) const ENTT_NOEXCEPT {} + +protected: +/** + * @brief Terminates a process with success if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ +void succeed() ENTT_NOEXCEPT { +if(alive()) { +current = state::succeeded; +} +} + +/** + * @brief Terminates a process with errors if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ +void fail() ENTT_NOEXCEPT { +if(alive()) { +current = state::failed; +} +} + +/** + * @brief Stops a process if it's in a running state. + * + * The function is idempotent and it does nothing if the process isn't + * running. + */ +void pause() ENTT_NOEXCEPT { +if(current == state::running) { +current = state::paused; +} +} + +/** + * @brief Restarts a process if it's paused. + * + * The function is idempotent and it does nothing if the process isn't + * paused. + */ +void unpause() ENTT_NOEXCEPT { +if(current == state::paused) { +current = state::running; +} +} + +public: +/*! @brief Type used to provide elapsed time. */ +using delta_type = Delta; + +/*! @brief Default destructor. */ +virtual ~process() ENTT_NOEXCEPT { +static_assert(std::is_base_of_v<process, Derived>, "Incorrect use of the class template"); +} + +/** + * @brief Aborts a process if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + * + * @param immediately Requests an immediate operation. + */ +void abort(const bool immediately = false) { +if(alive()) { +current = state::aborted; + +if(immediately) { +tick({}); +} +} +} + +/** + * @brief Returns true if a process is either running or paused. + * @return True if the process is still alive, false otherwise. + */ +[[nodiscard]] bool alive() const ENTT_NOEXCEPT { +return current == state::running || current == state::paused; +} + +/** + * @brief Returns true if a process is already terminated. + * @return True if the process is terminated, false otherwise. + */ +[[nodiscard]] bool finished() const ENTT_NOEXCEPT { +return current == state::finished; +} + +/** + * @brief Returns true if a process is currently paused. + * @return True if the process is paused, false otherwise. + */ +[[nodiscard]] bool paused() const ENTT_NOEXCEPT { +return current == state::paused; +} + +/** + * @brief Returns true if a process terminated with errors. + * @return True if the process terminated with errors, false otherwise. + */ +[[nodiscard]] bool rejected() const ENTT_NOEXCEPT { +return current == state::rejected; +} + +/** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ +void tick(const Delta delta, void *data = nullptr) { +switch(current) { +case state::uninitialized: +next(std::integral_constant<state, state::uninitialized>{}); +current = state::running; +break; +case state::running: +next(std::integral_constant<state, state::running>{}, delta, data); +break; +default: +// suppress warnings +break; +} + +// if it's dead, it must be notified and removed immediately +switch(current) { +case state::succeeded: +next(std::integral_constant<state, state::succeeded>{}); +current = state::finished; +break; +case state::failed: +next(std::integral_constant<state, state::failed>{}); +current = state::rejected; +break; +case state::aborted: +next(std::integral_constant<state, state::aborted>{}); +current = state::rejected; +break; +default: +// suppress warnings +break; +} +} + +private: +state current{state::uninitialized}; +}; + +/** + * @brief Adaptor for lambdas and functors to turn them into processes. + * + * Lambdas and functors can't be used directly with a scheduler for they are not + * properly defined processes with managed life cycles.<br/> + * This class helps in filling the gap and turning lambdas and functors into + * full featured processes usable by a scheduler. + * + * The signature of the function call operator should be equivalent to the + * following: + * + * @code{.cpp} + * void(Delta delta, void *data, auto succeed, auto fail); + * @endcode + * + * Where: + * + * * `delta` is the elapsed time. + * * `data` is an opaque pointer to user data if any, `nullptr` otherwise. + * * `succeed` is a function to call when a process terminates with success. + * * `fail` is a function to call when a process terminates with errors. + * + * The signature of the function call operator of both `succeed` and `fail` + * is equivalent to the following: + * + * @code{.cpp} + * void(); + * @endcode + * + * Usually users shouldn't worry about creating adaptors. A scheduler will + * create them internally each and avery time a lambda or a functor is used as + * a process. + * + * @sa process + * @sa scheduler + * + * @tparam Func Actual type of process. + * @tparam Delta Type to use to provide elapsed time. + */ +template<typename Func, typename Delta> +struct process_adaptor: process<process_adaptor<Func, Delta>, Delta>, private Func { +/** + * @brief Constructs a process adaptor from a lambda or a functor. + * @tparam Args Types of arguments to use to initialize the actual process. + * @param args Parameters to use to initialize the actual process. + */ +template<typename... Args> +process_adaptor(Args &&...args) +: Func{std::forward<Args>(args)...} {} + +/** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ +void update(const Delta delta, void *data) { +Func::operator()( +delta, +data, +[this]() { this->succeed(); }, +[this]() { this->fail(); }); +} +}; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Cooperative scheduler for processes. + * + * A cooperative scheduler runs processes and helps managing their life cycles. + * + * Each process is invoked once per tick. If a process terminates, it's + * removed automatically from the scheduler and it's never invoked again.<br/> + * A process can also have a child. In this case, the process is replaced with + * its child when it terminates if it returns with success. In case of errors, + * both the process and its child are discarded. + * + * Example of use (pseudocode): + * + * @code{.cpp} + * scheduler.attach([](auto delta, void *, auto succeed, auto fail) { + * // code + * }).then<my_process>(arguments...); + * @endcode + * + * In order to invoke all scheduled processes, call the `update` member function + * passing it the elapsed time to forward to the tasks. + * + * @sa process + * + * @tparam Delta Type to use to provide elapsed time. + */ +template<typename Delta> +class 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 next_type = std::unique_ptr<process_handler>; + +instance_type instance; +update_fn_type *update; +abort_fn_type *abort; +next_type next; +}; + +struct continuation { +continuation(process_handler *ref) ENTT_NOEXCEPT +: handler{ref} {} + +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}); +handler = handler->next.get(); +return *this; +} + +template<typename Func> +continuation then(Func &&func) { +return then<process_adaptor<std::decay_t<Func>, Delta>>(std::forward<Func>(func)); +} + +private: +process_handler *handler; +}; + +template<typename Proc> +[[nodiscard]] static bool update(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); + +if(process->rejected()) { +return true; +} else if(process->finished()) { +if(auto &&handler = owner.handlers[pos]; handler.next) { +handler = std::move(*handler.next); +// forces the process to exit the uninitialized state +return handler.update(owner, pos, {}, nullptr); +} + +return true; +} + +return false; +} + +template<typename Proc> +static void abort(scheduler &owner, std::size_t pos, const bool immediately) { +static_cast<Proc *>(owner.handlers[pos].instance.get())->abort(immediately); +} + +template<typename Proc> +static void deleter(void *proc) { +delete static_cast<Proc *>(proc); +} + +public: +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; + +/*! @brief Default constructor. */ +scheduler() = default; + +/*! @brief Default move constructor. */ +scheduler(scheduler &&) = default; + +/*! @brief Default move assignment operator. @return This scheduler. */ +scheduler &operator=(scheduler &&) = default; + +/** + * @brief Number of processes currently scheduled. + * @return Number of processes currently scheduled. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return handlers.size(); +} + +/** + * @brief Returns true if at least a process is currently scheduled. + * @return True if there are scheduled processes, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return handlers.empty(); +} + +/** + * @brief Discards all scheduled processes. + * + * Processes aren't aborted. They are discarded along with their children + * and never executed again. + */ +void clear() { +handlers.clear(); +} + +/** + * @brief Schedules a process for the next tick. + * + * Returned value is an opaque object that can be used to attach a child to + * the given process. The child is automatically scheduled when the process + * terminates and only if the process returns with success. + * + * Example of use (pseudocode): + * + * @code{.cpp} + * // schedules a task in the form of a process class + * scheduler.attach<my_process>(arguments...) + * // appends a child in the form of a lambda function + * .then([](auto delta, void *, auto succeed, auto fail) { + * // code + * }) + * // appends a child in the form of another process class + * .then<my_other_process>(); + * @endcode + * + * @tparam Proc Type of process to schedule. + * @tparam Args Types of arguments to use to initialize the process. + * @param args Parameters to use to initialize the process. + * @return An opaque object to use to concatenate processes. + */ +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}); +// forces the process to exit the uninitialized state +ref.update(*this, handlers.size() - 1u, {}, nullptr); +return continuation{&handlers.back()}; +} + +/** + * @brief Schedules a process for the next tick. + * + * A process can be either a lambda or a functor. The scheduler wraps both + * of them in a process adaptor internally.<br/> + * The signature of the function call operator should be equivalent to the + * following: + * + * @code{.cpp} + * void(Delta delta, void *data, auto succeed, auto fail); + * @endcode + * + * Where: + * + * * `delta` is the elapsed time. + * * `data` is an opaque pointer to user data if any, `nullptr` otherwise. + * * `succeed` is a function to call when a process terminates with success. + * * `fail` is a function to call when a process terminates with errors. + * + * The signature of the function call operator of both `succeed` and `fail` + * is equivalent to the following: + * + * @code{.cpp} + * void(); + * @endcode + * + * Returned value is an opaque object that can be used to attach a child to + * the given process. The child is automatically scheduled when the process + * terminates and only if the process returns with success. + * + * Example of use (pseudocode): + * + * @code{.cpp} + * // schedules a task in the form of a lambda function + * scheduler.attach([](auto delta, void *, auto succeed, auto fail) { + * // code + * }) + * // appends a child in the form of another lambda function + * .then([](auto delta, void *, auto succeed, auto fail) { + * // code + * }) + * // appends a child in the form of a process class + * .then<my_process>(arguments...); + * @endcode + * + * @sa process_adaptor + * + * @tparam Func Type of process to schedule. + * @param func Either a lambda or a functor to use as a process. + * @return An opaque object to use to concatenate processes. + */ +template<typename Func> +auto attach(Func &&func) { +using Proc = process_adaptor<std::decay_t<Func>, Delta>; +return attach<Proc>(std::forward<Func>(func)); +} + +/** + * @brief Updates all scheduled processes. + * + * All scheduled processes are executed in no specific order.<br/> + * If a process terminates with success, it's replaced with its child, if + * any. Otherwise, if a process terminates with an error, it's removed along + * with its child. + * + * @param delta Elapsed time. + * @param data Optional data. + */ +void update(const Delta delta, void *data = nullptr) { +for(auto pos = handlers.size(); pos; --pos) { +const auto curr = pos - 1u; + +if(const auto dead = handlers[curr].update(*this, curr, delta, data); dead) { +std::swap(handlers[curr], handlers.back()); +handlers.pop_back(); +} +} +} + +/** + * @brief Aborts all scheduled processes. + * + * Unless an immediate operation is requested, the abort is scheduled for + * the next tick. Processes won't be executed anymore in any case.<br/> + * Once a process is fully aborted and thus finished, it's discarded along + * with its child, if any. + * + * @param immediately Requests an immediate operation. + */ +void abort(const bool immediately = false) { +for(auto pos = handlers.size(); pos; --pos) { +const auto curr = pos - 1u; +handlers[curr].abort(*this, curr, immediately); +} +} + +private: +std::vector<process_handler> handlers{}; +}; + +} // namespace entt + +#endif + +// #include "resource/cache.hpp" +#ifndef ENTT_RESOURCE_RESOURCE_CACHE_HPP +#define ENTT_RESOURCE_RESOURCE_CACHE_HPP + +#include <cstddef> +#include <functional> +#include <iterator> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include <algorithm> +#include <cmath> +#include <cstddef> +#include <functional> +#include <iterator> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include <cstddef> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include <cstdint> +#include <type_traits> +// #include "../config/config.h" + + +namespace entt { + +template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)> +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type, std::size_t, typename = void> +struct compressed_pair_element { +using reference = Type &; +using const_reference = const Type &; + +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>> +compressed_pair_element() +: value{} {} + +template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>> +compressed_pair_element(Args &&args) +: value{std::forward<Args>(args)} {} + +template<typename... Args, std::size_t... Index> +compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) +: value{std::forward<Args>(std::get<Index>(args))...} {} + +[[nodiscard]] reference get() ENTT_NOEXCEPT { +return value; +} + +[[nodiscard]] const_reference get() const ENTT_NOEXCEPT { +return value; +} + +private: +Type value; +}; + +template<typename Type, std::size_t Tag> +struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type { +using reference = Type &; +using const_reference = const Type &; +using base_type = Type; + +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>> +compressed_pair_element() +: base_type{} {} + +template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>> +compressed_pair_element(Args &&args) +: base_type{std::forward<Args>(args)} {} + +template<typename... Args, std::size_t... Index> +compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) +: base_type{std::forward<Args>(std::get<Index>(args))...} {} + +[[nodiscard]] reference get() ENTT_NOEXCEPT { +return *this; +} + +[[nodiscard]] const_reference get() const ENTT_NOEXCEPT { +return *this; +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +class compressed_pair final +: internal::compressed_pair_element<First, 0u>, +internal::compressed_pair_element<Second, 1u> { +using first_base = internal::compressed_pair_element<First, 0u>; +using second_base = internal::compressed_pair_element<Second, 1u>; + +public: +/*! @brief The type of the first element that the pair stores. */ +using first_type = First; +/*! @brief The type of the second element that the pair stores. */ +using second_type = Second; + +/** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>> +constexpr compressed_pair() +: first_base{}, +second_base{} {} + +/** + * @brief Copy constructor. + * @param other The instance to copy from. + */ +constexpr compressed_pair(const compressed_pair &other) = default; + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +constexpr compressed_pair(compressed_pair &&other) = default; + +/** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ +template<typename Arg, typename Other> +constexpr compressed_pair(Arg &&arg, Other &&other) +: first_base{std::forward<Arg>(arg)}, +second_base{std::forward<Other>(other)} {} + +/** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ +template<typename... Args, typename... Other> +constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) +: first_base{std::move(args), std::index_sequence_for<Args...>{}}, +second_base{std::move(other), std::index_sequence_for<Other...>{}} {} + +/** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ +constexpr compressed_pair &operator=(const compressed_pair &other) = default; + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ +constexpr compressed_pair &operator=(compressed_pair &&other) = default; + +/** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ +[[nodiscard]] first_type &first() ENTT_NOEXCEPT { +return static_cast<first_base &>(*this).get(); +} + +/*! @copydoc first */ +[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { +return static_cast<const first_base &>(*this).get(); +} + +/** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ +[[nodiscard]] second_type &second() ENTT_NOEXCEPT { +return static_cast<second_base &>(*this).get(); +} + +/*! @copydoc second */ +[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { +return static_cast<const second_base &>(*this).get(); +} + +/** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ +void swap(compressed_pair &other) { +using std::swap; +swap(first(), other.first()); +swap(second(), other.second()); +} + +/** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ +template<std::size_t Index> +decltype(auto) get() ENTT_NOEXCEPT { +if constexpr(Index == 0u) { +return first(); +} else { +static_assert(Index == 1u, "Index out of bounds"); +return second(); +} +} + +/*! @copydoc get */ +template<std::size_t Index> +decltype(auto) get() const ENTT_NOEXCEPT { +if constexpr(Index == 0u) { +return first(); +} else { +static_assert(Index == 1u, "Index out of bounds"); +return second(); +} +} +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template<typename Type, typename Other> +compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template<typename First, typename Second> +inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) { +lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<size_t Index, typename First, typename Second> +struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> { +static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include <iterator> +#include <memory> +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template<typename Type> +struct input_iterator_pointer final { +/*! @brief Pointer type. */ +using pointer = Type *; + +/*! @brief Default copy constructor, deleted on purpose. */ +input_iterator_pointer(const input_iterator_pointer &) = delete; + +/*! @brief Default move constructor. */ +input_iterator_pointer(input_iterator_pointer &&) = default; + +/** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ +input_iterator_pointer(Type &&val) +: value{std::move(val)} {} + +/** + * @brief Default copy assignment operator, deleted on purpose. + * @return This proxy object. + */ +input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; + +/** + * @brief Default move assignment operator. + * @return This proxy object. + */ +input_iterator_pointer &operator=(input_iterator_pointer &&) = default; + +/** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ +[[nodiscard]] pointer operator->() ENTT_NOEXCEPT { +return std::addressof(value); +} + +private: +Type value; +}; + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template<typename It, typename Sentinel = It> +struct iterable_adaptor final { +/*! @brief Value type. */ +using value_type = typename std::iterator_traits<It>::value_type; +/*! @brief Iterator type. */ +using iterator = It; +/*! @brief Sentinel type. */ +using sentinel = Sentinel; + +/*! @brief Default constructor. */ +iterable_adaptor() = default; + +/** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ +iterable_adaptor(iterator from, sentinel to) +: first{from}, +last{to} {} + +/** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return first; +} + +/** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ +[[nodiscard]] sentinel end() const ENTT_NOEXCEPT { +return last; +} + +/*! @copydoc begin */ +[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { +return begin(); +} + +/*! @copydoc end */ +[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT { +return end(); +} + +private: +It first; +Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include <cstddef> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template<typename Type> +[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT { +if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) { +return ptr; +} else { +return to_address(std::forward<Type>(ptr).operator->()); +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) { +lhs = rhs; +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) { +lhs = std::move(rhs); +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); + +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) { +using std::swap; +swap(lhs, rhs); +} +} + +/** + * @brief Checks whether a value is a power of two or not. + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value. + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded"); +std::size_t curr = value - (value != 0u); + +for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) { +curr |= curr >> next; +} + +return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT { +ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); +return value & (mod - 1u); +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Args Types of arguments to use to construct the object. + */ +template<typename Allocator> +struct allocation_deleter: private Allocator { +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Pointer type. */ +using pointer = typename std::allocator_traits<Allocator>::pointer; + +/** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ +allocation_deleter(const allocator_type &alloc) +: Allocator{alloc} {} + +/** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ +void operator()(pointer ptr) { +using alloc_traits = typename std::allocator_traits<Allocator>; +alloc_traits::destroy(*this, to_address(ptr)); +alloc_traits::deallocate(*this, ptr, 1u); +} +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template<typename Type, typename Allocator, typename... Args> +auto allocate_unique(Allocator &allocator, Args &&...args) { +static_assert(!std::is_array_v<Type>, "Array types are not supported"); + +using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>; +using allocator_type = typename alloc_traits::allocator_type; + +allocator_type alloc{allocator}; +auto ptr = alloc_traits::allocate(alloc, 1u); + +ENTT_TRY { +alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...); +} +ENTT_CATCH { +alloc_traits::deallocate(alloc, ptr, 1u); +ENTT_THROW; +} + +return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc}; +} + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type> +struct uses_allocator_construction { +template<typename Allocator, typename... Params> +static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT { +if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) { +return std::forward_as_tuple(std::forward<Params>(params)...); +} else { +static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request"); + +if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) { +return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...); +} else { +static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request"); +return std::forward_as_tuple(std::forward<Params>(params)..., allocator); +} +} +} +}; + +template<typename Type, typename Other> +struct uses_allocator_construction<std::pair<Type, Other>> { +using type = std::pair<Type, Other>; + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT { +return std::make_tuple( +std::piecewise_construct, +std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)), +std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second))); +} + +template<typename Allocator> +static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second))); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT { +return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { +return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { +return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include <functional> +#include <memory> + +namespace entt { + +template< +typename Key, +typename Type, +typename = std::hash<Key>, +typename = std::equal_to<Key>, +typename = std::allocator<std::pair<const Key, Type>>> +class dense_map; + +template< +typename Type, +typename = std::hash<Type>, +typename = std::equal_to<Type>, +typename = std::allocator<Type>> +class dense_set; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Key, typename Type> +struct dense_map_node final { +using value_type = std::pair<Key, Type>; + +template<typename... Args> +dense_map_node(const std::size_t pos, Args &&...args) +: next{pos}, +element{std::forward<Args>(args)...} {} + +template<typename Allocator, typename... Args> +dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) +: next{pos}, +element{entt::make_obj_using_allocator<value_type>(allocator, std::forward<Args>(args)...)} {} + +template<typename Allocator> +dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) +: next{other.next}, +element{entt::make_obj_using_allocator<value_type>(allocator, other.element)} {} + +template<typename Allocator> +dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) +: next{other.next}, +element{entt::make_obj_using_allocator<value_type>(allocator, std::move(other.element))} {} + +std::size_t next; +value_type element; +}; + +template<typename It> +class dense_map_iterator final { +template<typename> +friend class dense_map_iterator; + +using first_type = decltype(std::as_const(std::declval<It>()->element.first)); +using second_type = decltype((std::declval<It>()->element.second)); + +public: +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; + +dense_map_iterator() ENTT_NOEXCEPT +: it{} {} + +dense_map_iterator(const It iter) ENTT_NOEXCEPT +: it{iter} {} + +template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> +dense_map_iterator(const dense_map_iterator<Other> &other) ENTT_NOEXCEPT +: it{other.it} {} + +dense_map_iterator &operator++() ENTT_NOEXCEPT { +return ++it, *this; +} + +dense_map_iterator operator++(int) ENTT_NOEXCEPT { +dense_map_iterator orig = *this; +return ++(*this), orig; +} + +dense_map_iterator &operator--() ENTT_NOEXCEPT { +return --it, *this; +} + +dense_map_iterator operator--(int) ENTT_NOEXCEPT { +dense_map_iterator orig = *this; +return operator--(), orig; +} + +dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { +it += value; +return *this; +} + +dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { +dense_map_iterator copy = *this; +return (copy += value); +} + +dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { +return (*this += -value); +} + +dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { +return (*this + -value); +} + +[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { +return {it[value].element.first, it[value].element.second}; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return {it->element.first, it->element.second}; +} + +template<typename ILhs, typename IRhs> +friend std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT; + +template<typename ILhs, typename IRhs> +friend bool operator==(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT; + +template<typename ILhs, typename IRhs> +friend bool operator<(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT; + +private: +It it; +}; + +template<typename ILhs, typename IRhs> +[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it - rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator==(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it == rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator!=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator<(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it < rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator>(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator<=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs > rhs); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator>=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +template<typename It> +class dense_map_local_iterator final { +template<typename> +friend class dense_map_local_iterator; + +using first_type = decltype(std::as_const(std::declval<It>()->element.first)); +using second_type = decltype((std::declval<It>()->element.second)); + +public: +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; + +dense_map_local_iterator() ENTT_NOEXCEPT +: it{}, +offset{} {} + +dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT +: it{iter}, +offset{pos} {} + +template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> +dense_map_local_iterator(const dense_map_local_iterator<Other> &other) ENTT_NOEXCEPT +: it{other.it}, +offset{other.offset} {} + +dense_map_local_iterator &operator++() ENTT_NOEXCEPT { +return offset = it[offset].next, *this; +} + +dense_map_local_iterator operator++(int) ENTT_NOEXCEPT { +dense_map_local_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return {it[offset].element.first, it[offset].element.second}; +} + +[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { +return offset; +} + +private: +It it; +std::size_t offset; +}; + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator==(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.index() == rhs.index(); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator!=(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Key, typename Type, typename Hash, typename KeyEqual, typename Allocator> +class dense_map { +static constexpr float default_threshold = 0.875f; +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>; +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>>; + +template<typename Other> +[[nodiscard]] std::size_t key_to_bucket(const Other &key) const ENTT_NOEXCEPT { +return fast_mod(sparse.second()(key), bucket_count()); +} + +template<typename Other> +[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { +for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { +if(packed.second()(it->first, key)) { +return begin() + static_cast<typename iterator::difference_type>(it.index()); +} +} + +return end(); +} + +template<typename Other> +[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { +for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { +if(packed.second()(it->first, key)) { +return cbegin() + static_cast<typename iterator::difference_type>(it.index()); +} +} + +return cend(); +} + +template<typename Other, typename... Args> +[[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { +const auto index = key_to_bucket(key); + +if(auto it = constrained_find(key, index); it != end()) { +return std::make_pair(it, false); +} + +packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward<Other>(key)), std::forward_as_tuple(std::forward<Args>(args)...)); +sparse.first()[index] = packed.first().size() - 1u; +rehash_if_required(); + +return std::make_pair(--end(), true); +} + +template<typename Other, typename Arg> +[[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { +const auto index = key_to_bucket(key); + +if(auto it = constrained_find(key, index); it != end()) { +it->second = std::forward<Arg>(value); +return std::make_pair(it, false); +} + +packed.first().emplace_back(sparse.first()[index], std::forward<Other>(key), std::forward<Arg>(value)); +sparse.first()[index] = packed.first().size() - 1u; +rehash_if_required(); + +return std::make_pair(--end(), true); +} + +void move_and_pop(const std::size_t pos) { +if(const auto last = size() - 1u; pos != last) { +packed.first()[pos] = std::move(packed.first().back()); +size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); +for(; *curr != last; curr = &packed.first()[*curr].next) {} +*curr = pos; +} + +packed.first().pop_back(); +} + +void rehash_if_required() { +if(size() > (bucket_count() * max_load_factor())) { +rehash(bucket_count() * 2u); +} +} + +public: +/*! @brief Key type of the container. */ +using key_type = Key; +/*! @brief Mapped type of the container. */ +using mapped_type = Type; +/*! @brief Key-value type of the container. */ +using value_type = std::pair<const Key, Type>; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Type of function to use to hash the keys. */ +using hasher = Hash; +/*! @brief Type of function to use to compare the keys for equality. */ +using key_equal = KeyEqual; +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Input iterator type. */ +using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>; +/*! @brief Constant input iterator type. */ +using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_iterator>; +/*! @brief Input iterator type. */ +using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::iterator>; +/*! @brief Constant input iterator type. */ +using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_iterator>; + +/*! @brief Default constructor. */ +dense_map() +: dense_map(minimum_capacity) {} + +/** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ +explicit dense_map(const allocator_type &allocator) +: dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param allocator The allocator to use. + */ +dense_map(const size_type bucket_count, const allocator_type &allocator) +: dense_map{bucket_count, hasher{}, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ +dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) +: dense_map{bucket_count, hash, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ +explicit dense_map(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) +: sparse{allocator, hash}, +packed{allocator, equal}, +threshold{default_threshold} { +rehash(bucket_count); +} + +/*! @brief Default copy constructor. */ +dense_map(const dense_map &) = default; + +/** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ +dense_map(const dense_map &other, const allocator_type &allocator) +: sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, +packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, +threshold{other.threshold} {} + +/*! @brief Default move constructor. */ +dense_map(dense_map &&) = default; + +/** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ +dense_map(dense_map &&other, const allocator_type &allocator) +: sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, +packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, +threshold{other.threshold} {} + +/** + * @brief Default copy assignment operator. + * @return This container. + */ +dense_map &operator=(const dense_map &) = default; + +/** + * @brief Default move assignment operator. + * @return This container. + */ +dense_map &operator=(dense_map &&) = default; + +/** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ +[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { +return sparse.first().get_allocator(); +} + +/** + * @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. + */ +[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { +return packed.first().begin(); +} + +/*! @copydoc cbegin */ +[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { +return cbegin(); +} + +/*! @copydoc begin */ +[[nodiscard]] iterator begin() ENTT_NOEXCEPT { +return packed.first().begin(); +} + +/** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the internal array. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the element following the last instance of the + * internal array. + */ +[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { +return packed.first().end(); +} + +/*! @copydoc cend */ +[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { +return cend(); +} + +/*! @copydoc end */ +[[nodiscard]] iterator end() ENTT_NOEXCEPT { +return packed.first().end(); +} + +/** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return packed.first().empty(); +} + +/** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return packed.first().size(); +} + +/*! @brief Clears the container. */ +void clear() ENTT_NOEXCEPT { +sparse.first().clear(); +packed.first().clear(); +rehash(0u); +} + +/** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +std::pair<iterator, bool> insert(const value_type &value) { +return insert_or_do_nothing(value.first, value.second); +} + +/*! @copydoc insert */ +std::pair<iterator, bool> insert(value_type &&value) { +return insert_or_do_nothing(std::move(value.first), std::move(value.second)); +} + +/** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ +template<typename Arg> +std::enable_if_t<std::is_constructible_v<value_type, Arg &&>, std::pair<iterator, bool>> +insert(Arg &&value) { +return insert_or_do_nothing(std::forward<Arg>(value).first, std::forward<Arg>(value).second); +} + +/** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ +template<typename It> +void insert(It first, It last) { +for(; first != last; ++first) { +insert(*first); +} +} + +/** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ +template<typename Arg> +std::pair<iterator, bool> insert_or_assign(const key_type &key, Arg &&value) { +return insert_or_overwrite(key, std::forward<Arg>(value)); +} + +/*! @copydoc insert_or_assign */ +template<typename Arg> +std::pair<iterator, bool> insert_or_assign(key_type &&key, Arg &&value) { +return insert_or_overwrite(std::move(key), std::forward<Arg>(value)); +} + +/** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +template<typename... Args> +std::pair<iterator, bool> emplace([[maybe_unused]] Args &&...args) { +if constexpr(sizeof...(Args) == 0u) { +return insert_or_do_nothing(key_type{}); +} else if constexpr(sizeof...(Args) == 1u) { +return insert_or_do_nothing(std::forward<Args>(args).first..., std::forward<Args>(args).second...); +} else if constexpr(sizeof...(Args) == 2u) { +return insert_or_do_nothing(std::forward<Args>(args)...); +} else { +auto &node = packed.first().emplace_back(packed.first().size(), std::forward<Args>(args)...); +const auto index = key_to_bucket(node.element.first); + +if(auto it = constrained_find(node.element.first, index); it != end()) { +packed.first().pop_back(); +return std::make_pair(it, false); +} + +std::swap(node.next, sparse.first()[index]); +rehash_if_required(); + +return std::make_pair(--end(), true); +} +} + +/** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +template<typename... Args> +std::pair<iterator, bool> try_emplace(const key_type &key, Args &&...args) { +return insert_or_do_nothing(key, std::forward<Args>(args)...); +} + +/*! @copydoc try_emplace */ +template<typename... Args> +std::pair<iterator, bool> try_emplace(key_type &&key, Args &&...args) { +return insert_or_do_nothing(std::move(key), std::forward<Args>(args)...); +} + +/** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ +iterator erase(const_iterator pos) { +const auto diff = pos - cbegin(); +erase(pos->first); +return begin() + diff; +} + +/** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ +iterator erase(const_iterator first, const_iterator last) { +const auto dist = first - cbegin(); + +for(auto from = last - cbegin(); from != dist; --from) { +erase(packed.first()[from - 1u].element.first); +} + +return (begin() + dist); +} + +/** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ +size_type erase(const key_type &key) { +for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) { +if(packed.second()(packed.first()[*curr].element.first, key)) { +const auto index = *curr; +*curr = packed.first()[*curr].next; +move_and_pop(index); +return 1u; +} +} + +return 0u; +} + +/** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ +void swap(dense_map &other) { +using std::swap; +swap(sparse, other.sparse); +swap(packed, other.packed); +swap(threshold, other.threshold); +} + +/** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ +[[nodiscard]] mapped_type &at(const key_type &key) { +auto it = find(key); +ENTT_ASSERT(it != end(), "Invalid key"); +return it->second; +} + +/*! @copydoc at */ +[[nodiscard]] const mapped_type &at(const key_type &key) const { +auto it = find(key); +ENTT_ASSERT(it != cend(), "Invalid key"); +return it->second; +} + +/** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ +[[nodiscard]] mapped_type &operator[](const key_type &key) { +return insert_or_do_nothing(key).first->second; +} + +/** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ +[[nodiscard]] mapped_type &operator[](key_type &&key) { +return insert_or_do_nothing(std::move(key)).first->second; +} + +/** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ +[[nodiscard]] iterator find(const key_type &key) { +return constrained_find(key, key_to_bucket(key)); +} + +/*! @copydoc find */ +[[nodiscard]] const_iterator find(const key_type &key) const { +return constrained_find(key, key_to_bucket(key)); +} + +/** + * @brief Finds an element with a key that compares _equivalent_ to a given + * value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>> +find(const Other &key) { +return constrained_find(key, key_to_bucket(key)); +} + +/*! @copydoc find */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>> +find(const Other &key) const { +return constrained_find(key, key_to_bucket(key)); +} + +/** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ +[[nodiscard]] bool contains(const key_type &key) const { +return (find(key) != cend()); +} + +/** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>> +contains(const Other &key) const { +return (find(key) != cend()); +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] const_local_iterator cbegin(const size_type index) const { +return {packed.first().begin(), sparse.first()[index]}; +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] const_local_iterator begin(const size_type index) const { +return cbegin(index); +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] local_iterator begin(const size_type index) { +return {packed.first().begin(), sparse.first()[index]}; +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { +return {packed.first().begin(), (std::numeric_limits<size_type>::max)()}; +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const { +return cend(index); +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { +return {packed.first().begin(), (std::numeric_limits<size_type>::max)()}; +} + +/** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ +[[nodiscard]] size_type bucket_count() const { +return sparse.first().size(); +} + +/** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ +[[nodiscard]] size_type max_bucket_count() const { +return sparse.first().max_size(); +} + +/** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ +[[nodiscard]] size_type bucket_size(const size_type index) const { +return static_cast<size_type>(std::distance(begin(index), end(index))); +} + +/** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ +[[nodiscard]] size_type bucket(const key_type &key) const { +return key_to_bucket(key); +} + +/** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ +[[nodiscard]] float load_factor() const { +return size() / static_cast<float>(bucket_count()); +} + +/** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ +[[nodiscard]] float max_load_factor() const { +return threshold; +} + +/** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ +void max_load_factor(const float value) { +ENTT_ASSERT(value > 0.f, "Invalid load factor"); +threshold = value; +rehash(0u); +} + +/** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param count New number of buckets. + */ +void rehash(const size_type count) { +auto value = (std::max)(count, minimum_capacity); +value = (std::max)(value, static_cast<size_type>(size() / max_load_factor())); + +if(const auto sz = next_power_of_two(value); sz != bucket_count()) { +sparse.first().resize(sz); +std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits<size_type>::max)()); + +for(size_type pos{}, last = size(); pos < last; ++pos) { +const auto index = key_to_bucket(packed.first()[pos].element.first); +packed.first()[pos].next = std::exchange(sparse.first()[index], pos); +} +} +} + +/** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param count New number of elements. + */ +void reserve(const size_type count) { +packed.first().reserve(count); +rehash(static_cast<size_type>(std::ceil(count / max_load_factor()))); +} + +/** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ +[[nodiscard]] hasher hash_function() const { +return sparse.second(); +} + +/** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ +[[nodiscard]] key_equal key_eq() const { +return packed.second(); +} + +private: +compressed_pair<sparse_container_type, hasher> sparse; +compressed_pair<packed_container_type, key_equal> packed; +float threshold; +}; + +} // namespace entt + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace std { + +template<typename Key, typename Value, typename Allocator> +struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator> +: std::true_type {}; + +} // namespace std + +/** + * Internal details not to be documented. + * @endcond + */ + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include <cstddef> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include <cstdint> +#include <type_traits> +// #include "../config/config.h" + + +namespace entt { + +template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)> +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type, std::size_t, typename = void> +struct compressed_pair_element { +using reference = Type &; +using const_reference = const Type &; + +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>> +compressed_pair_element() +: value{} {} + +template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>> +compressed_pair_element(Args &&args) +: value{std::forward<Args>(args)} {} + +template<typename... Args, std::size_t... Index> +compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) +: value{std::forward<Args>(std::get<Index>(args))...} {} + +[[nodiscard]] reference get() ENTT_NOEXCEPT { +return value; +} + +[[nodiscard]] const_reference get() const ENTT_NOEXCEPT { +return value; +} + +private: +Type value; +}; + +template<typename Type, std::size_t Tag> +struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type { +using reference = Type &; +using const_reference = const Type &; +using base_type = Type; + +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>> +compressed_pair_element() +: base_type{} {} + +template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>> +compressed_pair_element(Args &&args) +: base_type{std::forward<Args>(args)} {} + +template<typename... Args, std::size_t... Index> +compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) +: base_type{std::forward<Args>(std::get<Index>(args))...} {} + +[[nodiscard]] reference get() ENTT_NOEXCEPT { +return *this; +} + +[[nodiscard]] const_reference get() const ENTT_NOEXCEPT { +return *this; +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +class compressed_pair final +: internal::compressed_pair_element<First, 0u>, +internal::compressed_pair_element<Second, 1u> { +using first_base = internal::compressed_pair_element<First, 0u>; +using second_base = internal::compressed_pair_element<Second, 1u>; + +public: +/*! @brief The type of the first element that the pair stores. */ +using first_type = First; +/*! @brief The type of the second element that the pair stores. */ +using second_type = Second; + +/** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>> +constexpr compressed_pair() +: first_base{}, +second_base{} {} + +/** + * @brief Copy constructor. + * @param other The instance to copy from. + */ +constexpr compressed_pair(const compressed_pair &other) = default; + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +constexpr compressed_pair(compressed_pair &&other) = default; + +/** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ +template<typename Arg, typename Other> +constexpr compressed_pair(Arg &&arg, Other &&other) +: first_base{std::forward<Arg>(arg)}, +second_base{std::forward<Other>(other)} {} + +/** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ +template<typename... Args, typename... Other> +constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) +: first_base{std::move(args), std::index_sequence_for<Args...>{}}, +second_base{std::move(other), std::index_sequence_for<Other...>{}} {} + +/** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ +constexpr compressed_pair &operator=(const compressed_pair &other) = default; + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ +constexpr compressed_pair &operator=(compressed_pair &&other) = default; + +/** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ +[[nodiscard]] first_type &first() ENTT_NOEXCEPT { +return static_cast<first_base &>(*this).get(); +} + +/*! @copydoc first */ +[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { +return static_cast<const first_base &>(*this).get(); +} + +/** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ +[[nodiscard]] second_type &second() ENTT_NOEXCEPT { +return static_cast<second_base &>(*this).get(); +} + +/*! @copydoc second */ +[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { +return static_cast<const second_base &>(*this).get(); +} + +/** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ +void swap(compressed_pair &other) { +using std::swap; +swap(first(), other.first()); +swap(second(), other.second()); +} + +/** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ +template<std::size_t Index> +decltype(auto) get() ENTT_NOEXCEPT { +if constexpr(Index == 0u) { +return first(); +} else { +static_assert(Index == 1u, "Index out of bounds"); +return second(); +} +} + +/*! @copydoc get */ +template<std::size_t Index> +decltype(auto) get() const ENTT_NOEXCEPT { +if constexpr(Index == 0u) { +return first(); +} else { +static_assert(Index == 1u, "Index out of bounds"); +return second(); +} +} +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template<typename Type, typename Other> +compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template<typename First, typename Second> +inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) { +lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<size_t Index, typename First, typename Second> +struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> { +static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include <cstdint> +#include <type_traits> +// #include "../config/config.h" + + +namespace entt { + +template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)> +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include <iterator> +#include <memory> +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template<typename Type> +struct input_iterator_pointer final { +/*! @brief Pointer type. */ +using pointer = Type *; + +/*! @brief Default copy constructor, deleted on purpose. */ +input_iterator_pointer(const input_iterator_pointer &) = delete; + +/*! @brief Default move constructor. */ +input_iterator_pointer(input_iterator_pointer &&) = default; + +/** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ +input_iterator_pointer(Type &&val) +: value{std::move(val)} {} + +/** + * @brief Default copy assignment operator, deleted on purpose. + * @return This proxy object. + */ +input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; + +/** + * @brief Default move assignment operator. + * @return This proxy object. + */ +input_iterator_pointer &operator=(input_iterator_pointer &&) = default; + +/** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ +[[nodiscard]] pointer operator->() ENTT_NOEXCEPT { +return std::addressof(value); +} + +private: +Type value; +}; + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template<typename It, typename Sentinel = It> +struct iterable_adaptor final { +/*! @brief Value type. */ +using value_type = typename std::iterator_traits<It>::value_type; +/*! @brief Iterator type. */ +using iterator = It; +/*! @brief Sentinel type. */ +using sentinel = Sentinel; + +/*! @brief Default constructor. */ +iterable_adaptor() = default; + +/** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ +iterable_adaptor(iterator from, sentinel to) +: first{from}, +last{to} {} + +/** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return first; +} + +/** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ +[[nodiscard]] sentinel end() const ENTT_NOEXCEPT { +return last; +} + +/*! @copydoc begin */ +[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { +return begin(); +} + +/*! @copydoc end */ +[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT { +return end(); +} + +private: +It first; +Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { +/*! @brief Indicates that this is a transparent function object. */ +using is_transparent = void; + +/** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ +template<class Type> +[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { +return std::forward<Type>(value); +} +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template<typename Type, typename Class> +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template<typename Func> +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template<class... Func> +struct overloaded: Func... { +using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template<class... Func> +overloaded(Func...) -> overloaded<Func...>; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template<class Func> +struct y_combinator { +/** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ +y_combinator(Func recursive) +: func{std::move(recursive)} {} + +/** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ +template<class... Args> +decltype(auto) operator()(Args &&...args) const { +return func(*this, std::forward<Args>(args)...); +} + +/*! @copydoc operator()() */ +template<class... Args> +decltype(auto) operator()(Args &&...args) { +return func(*this, std::forward<Args>(args)...); +} + +private: +Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_RESOURCE_FWD_HPP +#define ENTT_RESOURCE_FWD_HPP + +#include <memory> + +namespace entt { + +template<typename> +struct resource_loader; + +template<typename Type, typename = resource_loader<Type>, typename = std::allocator<Type>> +class resource_cache; + +template<typename> +class resource; + +} // namespace entt + +#endif + +// #include "loader.hpp" +#ifndef ENTT_RESOURCE_LOADEr_HPP +#define ENTT_RESOURCE_LOADEr_HPP + +#include <memory> +#include <utility> +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Transparent loader for shared resources. + * @tparam Type Type of resources created by the loader. + */ +template<typename Type> +struct resource_loader { +/*! @brief Result type. */ +using result_type = std::shared_ptr<Type>; + +/** + * @brief Constructs a shared pointer to a resource from its arguments. + * @tparam Args Types of arguments to use to construct the resource. + * @param args Parameters to use to construct the resource. + * @return A shared pointer to a resource of the given type. + */ +template<typename... Args> +result_type operator()(Args &&...args) const { +return std::make_shared<Type>(std::forward<Args>(args)...); +} +}; + +} // namespace entt + +#endif + +// #include "resource.hpp" +#ifndef ENTT_RESOURCE_RESOURCE_HPP +#define ENTT_RESOURCE_RESOURCE_HPP + +#include <memory> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Basic resource handle. + * + * A handle wraps a resource and extends its lifetime. It also shares the same + * resource with all other handles constructed from the same element.<br/> + * As a rule of thumb, resources should never be copied nor moved. Handles are + * the way to go to push references around. + * + * @tparam Type Type of resource managed by a handle. + */ +template<typename Type> +class resource { +/*! @brief Resource handles are friends with each other. */ +template<typename> +friend class resource; + +template<typename Other> +static constexpr bool is_acceptable_v = !std::is_same_v<Type, Other> && std::is_constructible_v<Type &, Other &>; + +public: +/*! @brief Default constructor. */ +resource() ENTT_NOEXCEPT +: value{} {} + +/** + * @brief Creates a handle from a weak pointer, namely a resource. + * @param res A weak pointer to a resource. + */ +explicit resource(std::shared_ptr<Type> res) ENTT_NOEXCEPT +: value{std::move(res)} {} + +/*! @brief Default copy constructor. */ +resource(const resource &) ENTT_NOEXCEPT = default; + +/*! @brief Default move constructor. */ +resource(resource &&) ENTT_NOEXCEPT = default; + +/** + * @brief Aliasing constructor. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle with which to share ownership information. + * @param res Unrelated and unmanaged resources. + */ +template<typename Other> +resource(const resource<Other> &other, Type &res) ENTT_NOEXCEPT +: value{other.value, std::addressof(res)} {} + +/** + * @brief Copy constructs a handle which shares ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + */ +template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>> +resource(const resource<Other> &other) ENTT_NOEXCEPT +: value{other.value} {} + +/** + * @brief Move constructs a handle which takes ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + */ +template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>> +resource(resource<Other> &&other) ENTT_NOEXCEPT +: value{std::move(other.value)} {} + +/** + * @brief Default copy assignment operator. + * @return This resource handle. + */ +resource &operator=(const resource &) ENTT_NOEXCEPT = default; + +/** + * @brief Default move assignment operator. + * @return This resource handle. + */ +resource &operator=(resource &&) ENTT_NOEXCEPT = default; + +/** + * @brief Copy assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + * @return This resource handle. + */ +template<typename Other> +std::enable_if_t<is_acceptable_v<Other>, resource &> +operator=(const resource<Other> &other) ENTT_NOEXCEPT { +value = other.value; +return *this; +} + +/** + * @brief Move assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + * @return This resource handle. + */ +template<typename Other> +std::enable_if_t<is_acceptable_v<Other>, resource &> +operator=(resource<Other> &&other) ENTT_NOEXCEPT { +value = std::move(other.value); +return *this; +} + +/** + * @brief Returns a reference to the managed resource. + * + * @warning + * The behavior is undefined if the handle doesn't contain a resource. + * + * @return A reference to the managed resource. + */ +[[nodiscard]] Type &operator*() const ENTT_NOEXCEPT { +return *value; +} + +/*! @copydoc operator* */ +[[nodiscard]] operator Type &() const ENTT_NOEXCEPT { +return *value; +} + +/** + * @brief Returns a pointer to the managed resource. + * @return A pointer to the managed resource. + */ +[[nodiscard]] Type *operator->() const ENTT_NOEXCEPT { +return value.get(); +} + +/** + * @brief Returns true if a handle contains a resource, false otherwise. + * @return True if the handle contains a resource, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(value); +} + +/** + * @brief Returns the number of handles pointing the same resource. + * @return The number of handles pointing the same resource. + */ +[[nodiscard]] long use_count() const ENTT_NOEXCEPT { +return value.use_count(); +} + +private: +std::shared_ptr<Type> value; +}; + +/** + * @brief Compares two handles. + * @tparam Res Type of resource managed by the first handle. + * @tparam Other 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) ENTT_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. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return False if both handles refer to the same registry, true otherwise. + */ +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator!=(const resource<ILhs> &lhs, const resource<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type, typename It> +class resource_cache_iterator final { +template<typename, typename> +friend class resource_cache_iterator; + +public: +using value_type = std::pair<id_type, resource<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; + +resource_cache_iterator() ENTT_NOEXCEPT = default; + +resource_cache_iterator(const It iter) ENTT_NOEXCEPT +: it{iter} {} + +template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> +resource_cache_iterator(const resource_cache_iterator<std::remove_const_t<Type>, Other> &other) ENTT_NOEXCEPT +: it{other.it} {} + +resource_cache_iterator &operator++() ENTT_NOEXCEPT { +return ++it, *this; +} + +resource_cache_iterator operator++(int) ENTT_NOEXCEPT { +resource_cache_iterator orig = *this; +return ++(*this), orig; +} + +resource_cache_iterator &operator--() ENTT_NOEXCEPT { +return --it, *this; +} + +resource_cache_iterator operator--(int) ENTT_NOEXCEPT { +resource_cache_iterator orig = *this; +return operator--(), orig; +} + +resource_cache_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { +it += value; +return *this; +} + +resource_cache_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { +resource_cache_iterator copy = *this; +return (copy += value); +} + +resource_cache_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { +return (*this += -value); +} + +resource_cache_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { +return (*this + -value); +} + +[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { +return {it[value].first, resource<Type>{it[value].second}}; +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return (*this)[0]; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> +friend std::ptrdiff_t operator-(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) ENTT_NOEXCEPT; + +template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> +friend bool operator==(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) ENTT_NOEXCEPT; + +template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> +friend bool operator<(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) ENTT_NOEXCEPT; + +private: +It it; +}; + +template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> +[[nodiscard]] std::ptrdiff_t operator-(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it - rhs.it; +} + +template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> +[[nodiscard]] bool operator==(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it == rhs.it; +} + +template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> +[[nodiscard]] bool operator!=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> +[[nodiscard]] bool operator<(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it < rhs.it; +} + +template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> +[[nodiscard]] bool operator>(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> +[[nodiscard]] bool operator<=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs > rhs); +} + +template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> +[[nodiscard]] bool operator>=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Basic cache for resources of any type. + * @tparam Type Type of resources managed by a cache. + * @tparam Loader Type of loader used to create the resources. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Type, typename Loader, typename Allocator> +class resource_cache { +using alloc_traits = typename 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>; + +public: +/*! @brief Resource type. */ +using value_type = Type; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Loader type. */ +using loader_type = Loader; +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Input iterator type. */ +using iterator = internal::resource_cache_iterator<Type, typename container_type::iterator>; +/*! @brief Constant input iterator type. */ +using const_iterator = internal::resource_cache_iterator<const Type, typename container_type::const_iterator>; + +/*! @brief Default constructor. */ +resource_cache() +: resource_cache{loader_type{}} {} + +/** + * @brief Constructs an empty cache with a given allocator. + * @param allocator The allocator to use. + */ +explicit resource_cache(const allocator_type &allocator) +: resource_cache{loader_type{}, allocator} {} + +/** + * @brief Constructs an empty cache with a given allocator and loader. + * @param callable The loader to use. + * @param allocator The allocator to use. + */ +explicit resource_cache(const loader_type &callable, const allocator_type &allocator = allocator_type{}) +: pool{container_type{allocator}, callable} {} + +/*! @brief Default copy constructor. */ +resource_cache(const resource_cache &) = default; + +/** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ +resource_cache(const resource_cache &other, const allocator_type &allocator) +: pool{std::piecewise_construct, std::forward_as_tuple(other.pool.first(), allocator), std::forward_as_tuple(other.pool.second())} {} + +/*! @brief Default move constructor. */ +resource_cache(resource_cache &&) = default; + +/** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ +resource_cache(resource_cache &&other, const allocator_type &allocator) +: pool{std::piecewise_construct, std::forward_as_tuple(std::move(other.pool.first()), allocator), std::forward_as_tuple(std::move(other.pool.second()))} {} + +/** + * @brief Default copy assignment operator. + * @return This cache. + */ +resource_cache &operator=(const resource_cache &) = default; + +/** + * @brief Default move assignment operator. + * @return This cache. + */ +resource_cache &operator=(resource_cache &&) = default; + +/** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ +[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { +return pool.first().get_allocator(); +} + +/** + * @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()`. + * + * @return An iterator to the first instance of the internal cache. + */ +[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { +return pool.first().begin(); +} + +/*! @copydoc cbegin */ +[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { +return cbegin(); +} + +/*! @copydoc begin */ +[[nodiscard]] iterator begin() ENTT_NOEXCEPT { +return pool.first().begin(); +} + +/** + * @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. + */ +[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { +return pool.first().end(); +} + +/*! @copydoc cend */ +[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { +return cend(); +} + +/*! @copydoc end */ +[[nodiscard]] iterator end() ENTT_NOEXCEPT { +return pool.first().end(); +} + +/** + * @brief Returns true if a cache contains no resources, false otherwise. + * @return True if the cache contains no resources, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return pool.first().empty(); +} + +/** + * @brief Number of resources managed by a cache. + * @return Number of resources currently stored. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return pool.first().size(); +} + +/*! @brief Clears a cache. */ +void clear() ENTT_NOEXCEPT { +pool.first().clear(); +} + +/** + * @brief Loads a resource, if its identifier does not exist. + * + * Arguments are forwarded directly to the loader and _consumed_ only if the + * resource doesn't already exist. + * + * @warning + * If the resource isn't loaded correctly, the returned handle could be + * invalid and any use of it will result in undefined behavior. + * + * @tparam Args Types of arguments to use to load the resource if required. + * @param id Unique resource identifier. + * @param args Arguments to use to load the resource if required. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +template<typename... Args> +std::pair<iterator, bool> load(const id_type id, Args &&...args) { +if(auto it = pool.first().find(id); it != pool.first().end()) { +return {it, false}; +} + +return pool.first().emplace(id, pool.second()(std::forward<Args>(args)...)); +} + +/** + * @brief Force loads a resource, if its identifier does not exist. + * @copydetails load + */ +template<typename... Args> +std::pair<iterator, bool> force_load(const id_type id, Args &&...args) { +return {pool.first().insert_or_assign(id, pool.second()(std::forward<Args>(args)...)).first, true}; +} + +/** + * @brief Returns a handle for a given resource identifier. + * + * @warning + * There is no guarantee that the returned handle is valid.<br/> + * If it is not, any use will result in indefinite behavior. + * + * @param id Unique resource identifier. + * @return A handle for the given resource. + */ +[[nodiscard]] resource<const value_type> operator[](const id_type id) const { +if(auto it = pool.first().find(id); it != pool.first().cend()) { +return resource<const value_type>{it->second}; +} + +return {}; +} + +/*! @copydoc operator[] */ +[[nodiscard]] resource<value_type> operator[](const id_type id) { +if(auto it = pool.first().find(id); it != pool.first().end()) { +return resource<value_type>{it->second}; +} + +return {}; +} + +/** + * @brief Checks if a cache contains a given identifier. + * @param id Unique resource identifier. + * @return True if the cache contains the resource, false otherwise. + */ +[[nodiscard]] bool contains(const id_type id) const { +return pool.first().contains(id); +} + +/** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ +iterator erase(const_iterator pos) { +const auto it = pool.first().begin(); +return pool.first().erase(it + (pos - const_iterator{it})); +} + +/** + * @brief Removes the given elements from a cache. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ +iterator erase(const_iterator first, const_iterator last) { +const auto it = pool.first().begin(); +return pool.first().erase(it + (first - const_iterator{it}), it + (last - const_iterator{it})); +} + +/** + * @brief Removes the given elements from a cache. + * @param id Unique resource identifier. + * @return Number of resources erased (either 0 or 1). + */ +size_type erase(const id_type id) { +return pool.first().erase(id); +} + +/** + * @brief Returns the loader used to create resources. + * @return The loader used to create resources. + */ +[[nodiscard]] loader_type loader() const { +return pool.second(); +} + +private: +compressed_pair<container_type, loader_type> pool; +}; + +} // namespace entt + +#endif + +// #include "resource/loader.hpp" +#ifndef ENTT_RESOURCE_LOADEr_HPP +#define ENTT_RESOURCE_LOADEr_HPP + +#include <memory> +#include <utility> +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Transparent loader for shared resources. + * @tparam Type Type of resources created by the loader. + */ +template<typename Type> +struct resource_loader { +/*! @brief Result type. */ +using result_type = std::shared_ptr<Type>; + +/** + * @brief Constructs a shared pointer to a resource from its arguments. + * @tparam Args Types of arguments to use to construct the resource. + * @param args Parameters to use to construct the resource. + * @return A shared pointer to a resource of the given type. + */ +template<typename... Args> +result_type operator()(Args &&...args) const { +return std::make_shared<Type>(std::forward<Args>(args)...); +} +}; + +} // namespace entt + +#endif + +// #include "resource/resource.hpp" +#ifndef ENTT_RESOURCE_RESOURCE_HPP +#define ENTT_RESOURCE_RESOURCE_HPP + +#include <memory> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Basic resource handle. + * + * A handle wraps a resource and extends its lifetime. It also shares the same + * resource with all other handles constructed from the same element.<br/> + * As a rule of thumb, resources should never be copied nor moved. Handles are + * the way to go to push references around. + * + * @tparam Type Type of resource managed by a handle. + */ +template<typename Type> +class resource { +/*! @brief Resource handles are friends with each other. */ +template<typename> +friend class resource; + +template<typename Other> +static constexpr bool is_acceptable_v = !std::is_same_v<Type, Other> && std::is_constructible_v<Type &, Other &>; + +public: +/*! @brief Default constructor. */ +resource() ENTT_NOEXCEPT +: value{} {} + +/** + * @brief Creates a handle from a weak pointer, namely a resource. + * @param res A weak pointer to a resource. + */ +explicit resource(std::shared_ptr<Type> res) ENTT_NOEXCEPT +: value{std::move(res)} {} + +/*! @brief Default copy constructor. */ +resource(const resource &) ENTT_NOEXCEPT = default; + +/*! @brief Default move constructor. */ +resource(resource &&) ENTT_NOEXCEPT = default; + +/** + * @brief Aliasing constructor. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle with which to share ownership information. + * @param res Unrelated and unmanaged resources. + */ +template<typename Other> +resource(const resource<Other> &other, Type &res) ENTT_NOEXCEPT +: value{other.value, std::addressof(res)} {} + +/** + * @brief Copy constructs a handle which shares ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + */ +template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>> +resource(const resource<Other> &other) ENTT_NOEXCEPT +: value{other.value} {} + +/** + * @brief Move constructs a handle which takes ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + */ +template<typename Other, typename = std::enable_if_t<is_acceptable_v<Other>>> +resource(resource<Other> &&other) ENTT_NOEXCEPT +: value{std::move(other.value)} {} + +/** + * @brief Default copy assignment operator. + * @return This resource handle. + */ +resource &operator=(const resource &) ENTT_NOEXCEPT = default; + +/** + * @brief Default move assignment operator. + * @return This resource handle. + */ +resource &operator=(resource &&) ENTT_NOEXCEPT = default; + +/** + * @brief Copy assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + * @return This resource handle. + */ +template<typename Other> +std::enable_if_t<is_acceptable_v<Other>, resource &> +operator=(const resource<Other> &other) ENTT_NOEXCEPT { +value = other.value; +return *this; +} + +/** + * @brief Move assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + * @return This resource handle. + */ +template<typename Other> +std::enable_if_t<is_acceptable_v<Other>, resource &> +operator=(resource<Other> &&other) ENTT_NOEXCEPT { +value = std::move(other.value); +return *this; +} + +/** + * @brief Returns a reference to the managed resource. + * + * @warning + * The behavior is undefined if the handle doesn't contain a resource. + * + * @return A reference to the managed resource. + */ +[[nodiscard]] Type &operator*() const ENTT_NOEXCEPT { +return *value; +} + +/*! @copydoc operator* */ +[[nodiscard]] operator Type &() const ENTT_NOEXCEPT { +return *value; +} + +/** + * @brief Returns a pointer to the managed resource. + * @return A pointer to the managed resource. + */ +[[nodiscard]] Type *operator->() const ENTT_NOEXCEPT { +return value.get(); +} + +/** + * @brief Returns true if a handle contains a resource, false otherwise. + * @return True if the handle contains a resource, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(value); +} + +/** + * @brief Returns the number of handles pointing the same resource. + * @return The number of handles pointing the same resource. + */ +[[nodiscard]] long use_count() const ENTT_NOEXCEPT { +return value.use_count(); +} + +private: +std::shared_ptr<Type> value; +}; + +/** + * @brief Compares two handles. + * @tparam Res Type of resource managed by the first handle. + * @tparam Other 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) ENTT_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. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return False if both handles refer to the same registry, true otherwise. + */ +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator!=(const resource<ILhs> &lhs, const resource<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +} // namespace entt + +#endif + +// #include "signal/delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + +#include <cstddef> +#include <functional> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include <cstdint> +#include <type_traits> +// #include "../config/config.h" + + +namespace entt { + +template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)> +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_SIGNAL_FWD_HPP +#define ENTT_SIGNAL_FWD_HPP + +#include <memory> + +namespace entt { + +template<typename> +class delegate; + +template<typename = std::allocator<char>> +class basic_dispatcher; + +template<typename> +class emitter; + +class connection; + +struct scoped_connection; + +template<typename> +class sink; + +template<typename Type, typename = std::allocator<Type *>> +class sigh; + +/*! @brief Alias declaration for the most common use case. */ +using dispatcher = basic_dispatcher<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Ret, typename... Args> +auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template<typename Ret, typename Type, typename... Args, typename Other> +auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template<typename Class, typename Ret, typename... Args, typename... Other> +auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template<typename Class, typename Ret, typename... Args, typename... Other> +auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template<typename Class, typename Type, typename... Other> +auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template<typename... Type> +using function_pointer_t = decltype(internal::function_pointer(std::declval<Type>()...)); + +template<typename... Class, typename Ret, typename... Args> +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { +return std::index_sequence_for<Class..., Args...>{}; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @brief Used to wrap a function or a member of a specified type. */ +template<auto> +struct connect_arg_t {}; + +/*! @brief Constant of type connect_arg_t used to disambiguate calls. */ +template<auto Func> +inline constexpr connect_arg_t<Func> connect_arg{}; + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template<typename> +class delegate; + +/** + * @brief Utility class to use to send around functions and members. + * + * Unmanaged delegate for function pointers and members. Users of this class are + * in charge of disconnecting instances before deleting them. + * + * A delegate can be used as a general purpose invoker without memory overhead + * for free functions possibly with payloads and bound or unbound members. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template<typename Ret, typename... Args> +class delegate<Ret(Args...)> { +template<auto Candidate, std::size_t... Index> +[[nodiscard]] auto wrap(std::index_sequence<Index...>) ENTT_NOEXCEPT { +return [](const void *, Args... args) -> Ret { +[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...); +return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...)); +}; +} + +template<auto Candidate, typename Type, std::size_t... Index> +[[nodiscard]] auto wrap(Type &, std::index_sequence<Index...>) ENTT_NOEXCEPT { +return [](const void *payload, Args... args) -> Ret { +[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...); +Type *curr = static_cast<Type *>(const_cast<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))...)); +}; +} + +template<auto Candidate, typename Type, std::size_t... Index> +[[nodiscard]] auto wrap(Type *, std::index_sequence<Index...>) ENTT_NOEXCEPT { +return [](const void *payload, Args... args) -> Ret { +[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...); +Type *curr = static_cast<Type *>(const_cast<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))...)); +}; +} + +public: +/*! @brief Function type of the contained target. */ +using function_type = Ret(const void *, Args...); +/*! @brief Function type of the delegate. */ +using type = Ret(Args...); +/*! @brief Return type of the delegate. */ +using result_type = Ret; + +/*! @brief Default constructor. */ +delegate() ENTT_NOEXCEPT +: instance{nullptr}, +fn{nullptr} {} + +/** + * @brief Constructs a delegate and connects a free function or an unbound + * member. + * @tparam Candidate Function or member to connect to the delegate. + */ +template<auto Candidate> +delegate(connect_arg_t<Candidate>) ENTT_NOEXCEPT { +connect<Candidate>(); +} + +/** + * @brief Constructs a delegate and connects a free function with payload or + * a bound member. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ +template<auto Candidate, typename Type> +delegate(connect_arg_t<Candidate>, Type &&value_or_instance) ENTT_NOEXCEPT { +connect<Candidate>(std::forward<Type>(value_or_instance)); +} + +/** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ +delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { +connect(function, payload); +} + +/** + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. + */ +template<auto Candidate> +void connect() ENTT_NOEXCEPT { +instance = nullptr; + +if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) { +fn = [](const void *, Args... args) -> Ret { +return Ret(std::invoke(Candidate, std::forward<Args>(args)...)); +}; +} else if constexpr(std::is_member_pointer_v<decltype(Candidate)>) { +fn = wrap<Candidate>(internal::index_sequence_for<type_list_element_t<0, type_list<Args...>>>(internal::function_pointer_t<decltype(Candidate)>{})); +} else { +fn = wrap<Candidate>(internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate)>{})); +} +} + +/** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the delegate.<br/> + * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the delegate itself. + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid reference that fits the purpose. + */ +template<auto Candidate, typename Type> +void connect(Type &value_or_instance) ENTT_NOEXCEPT { +instance = &value_or_instance; + +if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, Args...>) { +fn = [](const void *payload, Args... args) -> Ret { +Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload)); +return Ret(std::invoke(Candidate, *curr, std::forward<Args>(args)...)); +}; +} else { +fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{})); +} +} + +/** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * @sa connect(Type &) + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + */ +template<auto Candidate, typename Type> +void connect(Type *value_or_instance) ENTT_NOEXCEPT { +instance = value_or_instance; + +if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, Args...>) { +fn = [](const void *payload, Args... args) -> Ret { +Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload)); +return Ret(std::invoke(Candidate, curr, std::forward<Args>(args)...)); +}; +} else { +fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{})); +} +} + +/** + * @brief Connects an user defined function with optional payload to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of an instance overcomes + * the one of the delegate.<br/> + * The payload is returned as the first argument to the target function in + * all cases. + * + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ +void connect(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { +instance = payload; +fn = function; +} + +/** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ +void reset() ENTT_NOEXCEPT { +instance = nullptr; +fn = nullptr; +} + +/** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ +[[nodiscard]] const void *data() const ENTT_NOEXCEPT { +return instance; +} + +/** + * @brief Triggers a delegate. + * + * The delegate invokes the underlying function and returns the result. + * + * @warning + * Attempting to trigger an invalid delegate results in undefined + * behavior. + * + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. + */ +Ret operator()(Args... args) const { +ENTT_ASSERT(static_cast<bool>(*this), "Uninitialized delegate"); +return fn(instance, std::forward<Args>(args)...); +} + +/** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +// no need to also test instance +return !(fn == nullptr); +} + +/** + * @brief Compares the contents of two delegates. + * @param other Delegate with which to compare. + * @return False if the two contents differ, true otherwise. + */ +[[nodiscard]] bool operator==(const delegate<Ret(Args...)> &other) const ENTT_NOEXCEPT { +return fn == other.fn && instance == other.instance; +} + +private: +const void *instance; +function_type *fn; +}; + +/** + * @brief Compares the contents of two delegates. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two contents differ, false otherwise. + */ +template<typename Ret, typename... Args> +[[nodiscard]] bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template<auto Candidate> +delegate(connect_arg_t<Candidate>) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate)>>>; + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + */ +template<auto Candidate, typename Type> +delegate(connect_arg_t<Candidate>, Type &&) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate), Type>>>; + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template<typename Ret, typename... Args> +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate<Ret(Args...)>; + +} // namespace entt + +#endif + +// #include "signal/dispatcher.hpp" +#ifndef ENTT_SIGNAL_DISPATCHER_HPP +#define ENTT_SIGNAL_DISPATCHER_HPP + +#include <algorithm> +#include <cstddef> +#include <memory> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include <algorithm> +#include <cmath> +#include <cstddef> +#include <functional> +#include <iterator> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include <cstddef> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include <atomic> +# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type> +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include <cstdint> +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include <cassert> +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include <cstdint> +#include <type_traits> +// #include "../config/config.h" + + +namespace entt { + +template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)> +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type, std::size_t, typename = void> +struct compressed_pair_element { +using reference = Type &; +using const_reference = const Type &; + +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>> +compressed_pair_element() +: value{} {} + +template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>> +compressed_pair_element(Args &&args) +: value{std::forward<Args>(args)} {} + +template<typename... Args, std::size_t... Index> +compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) +: value{std::forward<Args>(std::get<Index>(args))...} {} + +[[nodiscard]] reference get() ENTT_NOEXCEPT { +return value; +} + +[[nodiscard]] const_reference get() const ENTT_NOEXCEPT { +return value; +} + +private: +Type value; +}; + +template<typename Type, std::size_t Tag> +struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type { +using reference = Type &; +using const_reference = const Type &; +using base_type = Type; + +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>> +compressed_pair_element() +: base_type{} {} + +template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>> +compressed_pair_element(Args &&args) +: base_type{std::forward<Args>(args)} {} + +template<typename... Args, std::size_t... Index> +compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) +: base_type{std::forward<Args>(std::get<Index>(args))...} {} + +[[nodiscard]] reference get() ENTT_NOEXCEPT { +return *this; +} + +[[nodiscard]] const_reference get() const ENTT_NOEXCEPT { +return *this; +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +class compressed_pair final +: internal::compressed_pair_element<First, 0u>, +internal::compressed_pair_element<Second, 1u> { +using first_base = internal::compressed_pair_element<First, 0u>; +using second_base = internal::compressed_pair_element<Second, 1u>; + +public: +/*! @brief The type of the first element that the pair stores. */ +using first_type = First; +/*! @brief The type of the second element that the pair stores. */ +using second_type = Second; + +/** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>> +constexpr compressed_pair() +: first_base{}, +second_base{} {} + +/** + * @brief Copy constructor. + * @param other The instance to copy from. + */ +constexpr compressed_pair(const compressed_pair &other) = default; + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +constexpr compressed_pair(compressed_pair &&other) = default; + +/** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ +template<typename Arg, typename Other> +constexpr compressed_pair(Arg &&arg, Other &&other) +: first_base{std::forward<Arg>(arg)}, +second_base{std::forward<Other>(other)} {} + +/** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ +template<typename... Args, typename... Other> +constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) +: first_base{std::move(args), std::index_sequence_for<Args...>{}}, +second_base{std::move(other), std::index_sequence_for<Other...>{}} {} + +/** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ +constexpr compressed_pair &operator=(const compressed_pair &other) = default; + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ +constexpr compressed_pair &operator=(compressed_pair &&other) = default; + +/** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ +[[nodiscard]] first_type &first() ENTT_NOEXCEPT { +return static_cast<first_base &>(*this).get(); +} + +/*! @copydoc first */ +[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { +return static_cast<const first_base &>(*this).get(); +} + +/** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ +[[nodiscard]] second_type &second() ENTT_NOEXCEPT { +return static_cast<second_base &>(*this).get(); +} + +/*! @copydoc second */ +[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { +return static_cast<const second_base &>(*this).get(); +} + +/** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ +void swap(compressed_pair &other) { +using std::swap; +swap(first(), other.first()); +swap(second(), other.second()); +} + +/** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ +template<std::size_t Index> +decltype(auto) get() ENTT_NOEXCEPT { +if constexpr(Index == 0u) { +return first(); +} else { +static_assert(Index == 1u, "Index out of bounds"); +return second(); +} +} + +/*! @copydoc get */ +template<std::size_t Index> +decltype(auto) get() const ENTT_NOEXCEPT { +if constexpr(Index == 0u) { +return first(); +} else { +static_assert(Index == 1u, "Index out of bounds"); +return second(); +} +} +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template<typename Type, typename Other> +compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template<typename First, typename Second> +inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) { +lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<size_t Index, typename First, typename Second> +struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> { +static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include <iterator> +#include <memory> +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template<typename Type> +struct input_iterator_pointer final { +/*! @brief Pointer type. */ +using pointer = Type *; + +/*! @brief Default copy constructor, deleted on purpose. */ +input_iterator_pointer(const input_iterator_pointer &) = delete; + +/*! @brief Default move constructor. */ +input_iterator_pointer(input_iterator_pointer &&) = default; + +/** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ +input_iterator_pointer(Type &&val) +: value{std::move(val)} {} + +/** + * @brief Default copy assignment operator, deleted on purpose. + * @return This proxy object. + */ +input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; + +/** + * @brief Default move assignment operator. + * @return This proxy object. + */ +input_iterator_pointer &operator=(input_iterator_pointer &&) = default; + +/** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ +[[nodiscard]] pointer operator->() ENTT_NOEXCEPT { +return std::addressof(value); +} + +private: +Type value; +}; + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template<typename It, typename Sentinel = It> +struct iterable_adaptor final { +/*! @brief Value type. */ +using value_type = typename std::iterator_traits<It>::value_type; +/*! @brief Iterator type. */ +using iterator = It; +/*! @brief Sentinel type. */ +using sentinel = Sentinel; + +/*! @brief Default constructor. */ +iterable_adaptor() = default; + +/** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ +iterable_adaptor(iterator from, sentinel to) +: first{from}, +last{to} {} + +/** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ +[[nodiscard]] iterator begin() const ENTT_NOEXCEPT { +return first; +} + +/** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ +[[nodiscard]] sentinel end() const ENTT_NOEXCEPT { +return last; +} + +/*! @copydoc begin */ +[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { +return begin(); +} + +/*! @copydoc end */ +[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT { +return end(); +} + +private: +It first; +Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include <cstddef> +#include <limits> +#include <memory> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template<typename Type> +[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT { +if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) { +return ptr; +} else { +return to_address(std::forward<Type>(ptr).operator->()); +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) { +lhs = rhs; +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) { +lhs = std::move(rhs); +} +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template<typename Allocator> +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { +ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); + +if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) { +using std::swap; +swap(lhs, rhs); +} +} + +/** + * @brief Checks whether a value is a power of two or not. + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value. + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded"); +std::size_t curr = value - (value != 0u); + +for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) { +curr |= curr >> next; +} + +return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT { +ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); +return value & (mod - 1u); +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Args Types of arguments to use to construct the object. + */ +template<typename Allocator> +struct allocation_deleter: private Allocator { +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Pointer type. */ +using pointer = typename std::allocator_traits<Allocator>::pointer; + +/** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ +allocation_deleter(const allocator_type &alloc) +: Allocator{alloc} {} + +/** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ +void operator()(pointer ptr) { +using alloc_traits = typename std::allocator_traits<Allocator>; +alloc_traits::destroy(*this, to_address(ptr)); +alloc_traits::deallocate(*this, ptr, 1u); +} +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template<typename Type, typename Allocator, typename... Args> +auto allocate_unique(Allocator &allocator, Args &&...args) { +static_assert(!std::is_array_v<Type>, "Array types are not supported"); + +using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>; +using allocator_type = typename alloc_traits::allocator_type; + +allocator_type alloc{allocator}; +auto ptr = alloc_traits::allocate(alloc, 1u); + +ENTT_TRY { +alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...); +} +ENTT_CATCH { +alloc_traits::deallocate(alloc, ptr, 1u); +ENTT_THROW; +} + +return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc}; +} + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type> +struct uses_allocator_construction { +template<typename Allocator, typename... Params> +static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT { +if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) { +return std::forward_as_tuple(std::forward<Params>(params)...); +} else { +static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request"); + +if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) { +return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...); +} else { +static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request"); +return std::forward_as_tuple(std::forward<Params>(params)..., allocator); +} +} +} +}; + +template<typename Type, typename Other> +struct uses_allocator_construction<std::pair<Type, Other>> { +using type = std::pair<Type, Other>; + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT { +return std::make_tuple( +std::piecewise_construct, +std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)), +std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second))); +} + +template<typename Allocator> +static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second))); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); +} + +template<typename Allocator, typename First, typename Second> +static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT { +return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT { +return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { +return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template<typename Type, typename Allocator, typename... Args> +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { +return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include <functional> +#include <memory> + +namespace entt { + +template< +typename Key, +typename Type, +typename = std::hash<Key>, +typename = std::equal_to<Key>, +typename = std::allocator<std::pair<const Key, Type>>> +class dense_map; + +template< +typename Type, +typename = std::hash<Type>, +typename = std::equal_to<Type>, +typename = std::allocator<Type>> +class dense_set; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Key, typename Type> +struct dense_map_node final { +using value_type = std::pair<Key, Type>; + +template<typename... Args> +dense_map_node(const std::size_t pos, Args &&...args) +: next{pos}, +element{std::forward<Args>(args)...} {} + +template<typename Allocator, typename... Args> +dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) +: next{pos}, +element{entt::make_obj_using_allocator<value_type>(allocator, std::forward<Args>(args)...)} {} + +template<typename Allocator> +dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) +: next{other.next}, +element{entt::make_obj_using_allocator<value_type>(allocator, other.element)} {} + +template<typename Allocator> +dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) +: next{other.next}, +element{entt::make_obj_using_allocator<value_type>(allocator, std::move(other.element))} {} + +std::size_t next; +value_type element; +}; + +template<typename It> +class dense_map_iterator final { +template<typename> +friend class dense_map_iterator; + +using first_type = decltype(std::as_const(std::declval<It>()->element.first)); +using second_type = decltype((std::declval<It>()->element.second)); + +public: +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; + +dense_map_iterator() ENTT_NOEXCEPT +: it{} {} + +dense_map_iterator(const It iter) ENTT_NOEXCEPT +: it{iter} {} + +template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> +dense_map_iterator(const dense_map_iterator<Other> &other) ENTT_NOEXCEPT +: it{other.it} {} + +dense_map_iterator &operator++() ENTT_NOEXCEPT { +return ++it, *this; +} + +dense_map_iterator operator++(int) ENTT_NOEXCEPT { +dense_map_iterator orig = *this; +return ++(*this), orig; +} + +dense_map_iterator &operator--() ENTT_NOEXCEPT { +return --it, *this; +} + +dense_map_iterator operator--(int) ENTT_NOEXCEPT { +dense_map_iterator orig = *this; +return operator--(), orig; +} + +dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { +it += value; +return *this; +} + +dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { +dense_map_iterator copy = *this; +return (copy += value); +} + +dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { +return (*this += -value); +} + +dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { +return (*this + -value); +} + +[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { +return {it[value].element.first, it[value].element.second}; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return {it->element.first, it->element.second}; +} + +template<typename ILhs, typename IRhs> +friend std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT; + +template<typename ILhs, typename IRhs> +friend bool operator==(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT; + +template<typename ILhs, typename IRhs> +friend bool operator<(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT; + +private: +It it; +}; + +template<typename ILhs, typename IRhs> +[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it - rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator==(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it == rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator!=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator<(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.it < rhs.it; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator>(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator<=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs > rhs); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator>=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +template<typename It> +class dense_map_local_iterator final { +template<typename> +friend class dense_map_local_iterator; + +using first_type = decltype(std::as_const(std::declval<It>()->element.first)); +using second_type = decltype((std::declval<It>()->element.second)); + +public: +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; + +dense_map_local_iterator() ENTT_NOEXCEPT +: it{}, +offset{} {} + +dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT +: it{iter}, +offset{pos} {} + +template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>> +dense_map_local_iterator(const dense_map_local_iterator<Other> &other) ENTT_NOEXCEPT +: it{other.it}, +offset{other.offset} {} + +dense_map_local_iterator &operator++() ENTT_NOEXCEPT { +return offset = it[offset].next, *this; +} + +dense_map_local_iterator operator++(int) ENTT_NOEXCEPT { +dense_map_local_iterator orig = *this; +return ++(*this), orig; +} + +[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { +return operator*(); +} + +[[nodiscard]] reference operator*() const ENTT_NOEXCEPT { +return {it[offset].element.first, it[offset].element.second}; +} + +[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { +return offset; +} + +private: +It it; +std::size_t offset; +}; + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator==(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return lhs.index() == rhs.index(); +} + +template<typename ILhs, typename IRhs> +[[nodiscard]] bool operator!=(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Key, typename Type, typename Hash, typename KeyEqual, typename Allocator> +class dense_map { +static constexpr float default_threshold = 0.875f; +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>; +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>>; + +template<typename Other> +[[nodiscard]] std::size_t key_to_bucket(const Other &key) const ENTT_NOEXCEPT { +return fast_mod(sparse.second()(key), bucket_count()); +} + +template<typename Other> +[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { +for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { +if(packed.second()(it->first, key)) { +return begin() + static_cast<typename iterator::difference_type>(it.index()); +} +} + +return end(); +} + +template<typename Other> +[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { +for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { +if(packed.second()(it->first, key)) { +return cbegin() + static_cast<typename iterator::difference_type>(it.index()); +} +} + +return cend(); +} + +template<typename Other, typename... Args> +[[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { +const auto index = key_to_bucket(key); + +if(auto it = constrained_find(key, index); it != end()) { +return std::make_pair(it, false); +} + +packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward<Other>(key)), std::forward_as_tuple(std::forward<Args>(args)...)); +sparse.first()[index] = packed.first().size() - 1u; +rehash_if_required(); + +return std::make_pair(--end(), true); +} + +template<typename Other, typename Arg> +[[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { +const auto index = key_to_bucket(key); + +if(auto it = constrained_find(key, index); it != end()) { +it->second = std::forward<Arg>(value); +return std::make_pair(it, false); +} + +packed.first().emplace_back(sparse.first()[index], std::forward<Other>(key), std::forward<Arg>(value)); +sparse.first()[index] = packed.first().size() - 1u; +rehash_if_required(); + +return std::make_pair(--end(), true); +} + +void move_and_pop(const std::size_t pos) { +if(const auto last = size() - 1u; pos != last) { +packed.first()[pos] = std::move(packed.first().back()); +size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); +for(; *curr != last; curr = &packed.first()[*curr].next) {} +*curr = pos; +} + +packed.first().pop_back(); +} + +void rehash_if_required() { +if(size() > (bucket_count() * max_load_factor())) { +rehash(bucket_count() * 2u); +} +} + +public: +/*! @brief Key type of the container. */ +using key_type = Key; +/*! @brief Mapped type of the container. */ +using mapped_type = Type; +/*! @brief Key-value type of the container. */ +using value_type = std::pair<const Key, Type>; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Type of function to use to hash the keys. */ +using hasher = Hash; +/*! @brief Type of function to use to compare the keys for equality. */ +using key_equal = KeyEqual; +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Input iterator type. */ +using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>; +/*! @brief Constant input iterator type. */ +using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_iterator>; +/*! @brief Input iterator type. */ +using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::iterator>; +/*! @brief Constant input iterator type. */ +using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_iterator>; + +/*! @brief Default constructor. */ +dense_map() +: dense_map(minimum_capacity) {} + +/** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ +explicit dense_map(const allocator_type &allocator) +: dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param allocator The allocator to use. + */ +dense_map(const size_type bucket_count, const allocator_type &allocator) +: dense_map{bucket_count, hasher{}, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ +dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) +: dense_map{bucket_count, hash, key_equal{}, allocator} {} + +/** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ +explicit dense_map(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) +: sparse{allocator, hash}, +packed{allocator, equal}, +threshold{default_threshold} { +rehash(bucket_count); +} + +/*! @brief Default copy constructor. */ +dense_map(const dense_map &) = default; + +/** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ +dense_map(const dense_map &other, const allocator_type &allocator) +: sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, +packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, +threshold{other.threshold} {} + +/*! @brief Default move constructor. */ +dense_map(dense_map &&) = default; + +/** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ +dense_map(dense_map &&other, const allocator_type &allocator) +: sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, +packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, +threshold{other.threshold} {} + +/** + * @brief Default copy assignment operator. + * @return This container. + */ +dense_map &operator=(const dense_map &) = default; + +/** + * @brief Default move assignment operator. + * @return This container. + */ +dense_map &operator=(dense_map &&) = default; + +/** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ +[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { +return sparse.first().get_allocator(); +} + +/** + * @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. + */ +[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { +return packed.first().begin(); +} + +/*! @copydoc cbegin */ +[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { +return cbegin(); +} + +/*! @copydoc begin */ +[[nodiscard]] iterator begin() ENTT_NOEXCEPT { +return packed.first().begin(); +} + +/** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the internal array. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the element following the last instance of the + * internal array. + */ +[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { +return packed.first().end(); +} + +/*! @copydoc cend */ +[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { +return cend(); +} + +/*! @copydoc end */ +[[nodiscard]] iterator end() ENTT_NOEXCEPT { +return packed.first().end(); +} + +/** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return packed.first().empty(); +} + +/** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return packed.first().size(); +} + +/*! @brief Clears the container. */ +void clear() ENTT_NOEXCEPT { +sparse.first().clear(); +packed.first().clear(); +rehash(0u); +} + +/** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +std::pair<iterator, bool> insert(const value_type &value) { +return insert_or_do_nothing(value.first, value.second); +} + +/*! @copydoc insert */ +std::pair<iterator, bool> insert(value_type &&value) { +return insert_or_do_nothing(std::move(value.first), std::move(value.second)); +} + +/** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ +template<typename Arg> +std::enable_if_t<std::is_constructible_v<value_type, Arg &&>, std::pair<iterator, bool>> +insert(Arg &&value) { +return insert_or_do_nothing(std::forward<Arg>(value).first, std::forward<Arg>(value).second); +} + +/** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ +template<typename It> +void insert(It first, It last) { +for(; first != last; ++first) { +insert(*first); +} +} + +/** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ +template<typename Arg> +std::pair<iterator, bool> insert_or_assign(const key_type &key, Arg &&value) { +return insert_or_overwrite(key, std::forward<Arg>(value)); +} + +/*! @copydoc insert_or_assign */ +template<typename Arg> +std::pair<iterator, bool> insert_or_assign(key_type &&key, Arg &&value) { +return insert_or_overwrite(std::move(key), std::forward<Arg>(value)); +} + +/** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +template<typename... Args> +std::pair<iterator, bool> emplace([[maybe_unused]] Args &&...args) { +if constexpr(sizeof...(Args) == 0u) { +return insert_or_do_nothing(key_type{}); +} else if constexpr(sizeof...(Args) == 1u) { +return insert_or_do_nothing(std::forward<Args>(args).first..., std::forward<Args>(args).second...); +} else if constexpr(sizeof...(Args) == 2u) { +return insert_or_do_nothing(std::forward<Args>(args)...); +} else { +auto &node = packed.first().emplace_back(packed.first().size(), std::forward<Args>(args)...); +const auto index = key_to_bucket(node.element.first); + +if(auto it = constrained_find(node.element.first, index); it != end()) { +packed.first().pop_back(); +return std::make_pair(it, false); +} + +std::swap(node.next, sparse.first()[index]); +rehash_if_required(); + +return std::make_pair(--end(), true); +} +} + +/** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ +template<typename... Args> +std::pair<iterator, bool> try_emplace(const key_type &key, Args &&...args) { +return insert_or_do_nothing(key, std::forward<Args>(args)...); +} + +/*! @copydoc try_emplace */ +template<typename... Args> +std::pair<iterator, bool> try_emplace(key_type &&key, Args &&...args) { +return insert_or_do_nothing(std::move(key), std::forward<Args>(args)...); +} + +/** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ +iterator erase(const_iterator pos) { +const auto diff = pos - cbegin(); +erase(pos->first); +return begin() + diff; +} + +/** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ +iterator erase(const_iterator first, const_iterator last) { +const auto dist = first - cbegin(); + +for(auto from = last - cbegin(); from != dist; --from) { +erase(packed.first()[from - 1u].element.first); +} + +return (begin() + dist); +} + +/** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ +size_type erase(const key_type &key) { +for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) { +if(packed.second()(packed.first()[*curr].element.first, key)) { +const auto index = *curr; +*curr = packed.first()[*curr].next; +move_and_pop(index); +return 1u; +} +} + +return 0u; +} + +/** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ +void swap(dense_map &other) { +using std::swap; +swap(sparse, other.sparse); +swap(packed, other.packed); +swap(threshold, other.threshold); +} + +/** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ +[[nodiscard]] mapped_type &at(const key_type &key) { +auto it = find(key); +ENTT_ASSERT(it != end(), "Invalid key"); +return it->second; +} + +/*! @copydoc at */ +[[nodiscard]] const mapped_type &at(const key_type &key) const { +auto it = find(key); +ENTT_ASSERT(it != cend(), "Invalid key"); +return it->second; +} + +/** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ +[[nodiscard]] mapped_type &operator[](const key_type &key) { +return insert_or_do_nothing(key).first->second; +} + +/** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ +[[nodiscard]] mapped_type &operator[](key_type &&key) { +return insert_or_do_nothing(std::move(key)).first->second; +} + +/** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ +[[nodiscard]] iterator find(const key_type &key) { +return constrained_find(key, key_to_bucket(key)); +} + +/*! @copydoc find */ +[[nodiscard]] const_iterator find(const key_type &key) const { +return constrained_find(key, key_to_bucket(key)); +} + +/** + * @brief Finds an element with a key that compares _equivalent_ to a given + * value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>> +find(const Other &key) { +return constrained_find(key, key_to_bucket(key)); +} + +/*! @copydoc find */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>> +find(const Other &key) const { +return constrained_find(key, key_to_bucket(key)); +} + +/** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ +[[nodiscard]] bool contains(const key_type &key) const { +return (find(key) != cend()); +} + +/** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ +template<typename Other> +[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>> +contains(const Other &key) const { +return (find(key) != cend()); +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] const_local_iterator cbegin(const size_type index) const { +return {packed.first().begin(), sparse.first()[index]}; +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] const_local_iterator begin(const size_type index) const { +return cbegin(index); +} + +/** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ +[[nodiscard]] local_iterator begin(const size_type index) { +return {packed.first().begin(), sparse.first()[index]}; +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { +return {packed.first().begin(), (std::numeric_limits<size_type>::max)()}; +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const { +return cend(index); +} + +/** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ +[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { +return {packed.first().begin(), (std::numeric_limits<size_type>::max)()}; +} + +/** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ +[[nodiscard]] size_type bucket_count() const { +return sparse.first().size(); +} + +/** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ +[[nodiscard]] size_type max_bucket_count() const { +return sparse.first().max_size(); +} + +/** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ +[[nodiscard]] size_type bucket_size(const size_type index) const { +return static_cast<size_type>(std::distance(begin(index), end(index))); +} + +/** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ +[[nodiscard]] size_type bucket(const key_type &key) const { +return key_to_bucket(key); +} + +/** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ +[[nodiscard]] float load_factor() const { +return size() / static_cast<float>(bucket_count()); +} + +/** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ +[[nodiscard]] float max_load_factor() const { +return threshold; +} + +/** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ +void max_load_factor(const float value) { +ENTT_ASSERT(value > 0.f, "Invalid load factor"); +threshold = value; +rehash(0u); +} + +/** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param count New number of buckets. + */ +void rehash(const size_type count) { +auto value = (std::max)(count, minimum_capacity); +value = (std::max)(value, static_cast<size_type>(size() / max_load_factor())); + +if(const auto sz = next_power_of_two(value); sz != bucket_count()) { +sparse.first().resize(sz); +std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits<size_type>::max)()); + +for(size_type pos{}, last = size(); pos < last; ++pos) { +const auto index = key_to_bucket(packed.first()[pos].element.first); +packed.first()[pos].next = std::exchange(sparse.first()[index], pos); +} +} +} + +/** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param count New number of elements. + */ +void reserve(const size_type count) { +packed.first().reserve(count); +rehash(static_cast<size_type>(std::ceil(count / max_load_factor()))); +} + +/** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ +[[nodiscard]] hasher hash_function() const { +return sparse.second(); +} + +/** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ +[[nodiscard]] key_equal key_eq() const { +return packed.second(); +} + +private: +compressed_pair<sparse_container_type, hasher> sparse; +compressed_pair<packed_container_type, key_equal> packed; +float threshold; +}; + +} // namespace entt + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace std { + +template<typename Key, typename Value, typename Allocator> +struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator> +: std::true_type {}; + +} // namespace std + +/** + * Internal details not to be documented. + * @endcond + */ + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include <cstddef> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template<std::size_t N> +struct choice_t +// Unfortunately, doxygen cannot parse such a construct. +: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template<std::size_t N> +inline constexpr choice_t<N> choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template<typename Type> +struct type_identity { +/*! @brief Identity type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template<typename Type> +using type_identity_t = typename type_identity<Type>::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. + */ +template<typename Type, typename = void> +struct size_of: std::integral_constant<std::size_t, 0u> {}; + +/*! @copydoc size_of */ +template<typename Type> +struct size_of<Type, std::void_t<decltype(sizeof(Type))>> +: std::integral_constant<std::size_t, sizeof(Type)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template<typename Type> +inline constexpr std::size_t size_of_v = size_of<Type>::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template<typename Type, typename> +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template<auto Value, typename> +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template<auto Value> +using integral_constant = std::integral_constant<decltype(Value), Value>; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template<id_type Value> +using tag = integral_constant<Value>; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list { +/*! @brief Type list type. */ +using type = type_list; +/*! @brief Compile-time number of elements in the type list. */ +static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<std::size_t Index, typename Type, typename... Other> +struct type_list_element<Index, type_list<Type, Other...>> +: type_list_element<Index - 1u, type_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template<typename Type, typename... Other> +struct type_list_element<0u, type_list<Type, Other...>> { +/*! @brief Searched type. */ +using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template<std::size_t Index, typename List> +using type_list_element_t = typename type_list_element<Index, List>::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template<typename... Type, typename... Other> +constexpr type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template<typename... Type, typename... Other, typename... List> +struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = typename type_list_cat<type_list<Type..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template<typename... Type> +struct type_list_cat<type_list<Type...>> { +/*! @brief A type list composed by the types of all the type lists. */ +using type = type_list<Type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template<typename... List> +using type_list_cat_t = typename type_list_cat<List...>::type; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename> +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template<typename Type, typename... Other> +struct type_list_unique<type_list<Type, Other...>> { +/*! @brief A type list without duplicate types. */ +using type = std::conditional_t< +(std::is_same_v<Type, Other> || ...), +typename type_list_unique<type_list<Other...>>::type, +type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>; +}; + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique<type_list<>> { +/*! @brief A type list without duplicate types. */ +using type = type_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template<typename Type> +using type_list_unique_t = typename type_list_unique<Type>::type; + +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +struct type_list_contains; + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template<typename... Type, typename Other> +struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template<typename List, typename Type> +inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value; + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template<typename... Type, typename... Other> +struct type_list_diff<type_list<Type...>, type_list<Other...>> { +/*! @brief A type list that is the difference between the two type lists. */ +using type = type_list_cat_t<std::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template<typename... List> +using type_list_diff_t = typename type_list_diff<List...>::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list { +/*! @brief Value list type. */ +using type = value_list; +/*! @brief Compile-time number of elements in the value list. */ +static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template<std::size_t, typename> +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<std::size_t Index, auto Value, auto... Other> +struct value_list_element<Index, value_list<Value, Other...>> +: value_list_element<Index - 1u, value_list<Other...>> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template<auto Value, auto... Other> +struct value_list_element<0u, value_list<Value, Other...>> { +/*! @brief Searched value. */ +static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template<std::size_t Index, typename List> +inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template<auto... Value, auto... Other> +constexpr value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) { +return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template<typename...> +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<>; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template<auto... Value, auto... Other, typename... List> +struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = typename value_list_cat<value_list<Value..., Other...>, List...>::type; +}; + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template<auto... Value> +struct value_list_cat<value_list<Value...>> { +/*! @brief A value list composed by the values of all the value lists. */ +using type = value_list<Value...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template<typename... List> +using value_list_cat_t = typename value_list_cat<List...>::type; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template<typename, typename> +struct is_applicable: std::false_type {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, template<typename...> class Tuple, typename... Args> +struct is_applicable<Func, const Tuple<Args...>>: std::is_invocable<Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Func, typename Args> +inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template<typename, typename, typename> +struct is_applicable_r: std::false_type {}; + +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename... Args> +struct is_applicable_r<Ret, Func, std::tuple<Args...>>: std::is_invocable_r<Ret, Func, Args...> {}; + +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template<typename Ret, typename Func, typename Args> +inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template<typename Type> +struct is_complete<Type, std::void_t<decltype(sizeof(Type))>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_complete_v = is_complete<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_iterator: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_iterator_category: std::false_type {}; + +template<typename Type> +struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template<typename Type> +struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>> +: internal::has_iterator_category<Type> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_iterator_v = is_iterator<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template<typename Type> +struct is_ebco_eligible +: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template<typename Type> +struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_transparent_v = is_transparent<Type>::value; + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template<typename Type, typename = void> +struct is_equality_comparable: std::false_type {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename, typename = void> +struct has_tuple_size_value: std::false_type {}; + +template<typename Type> +struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {}; + +template<typename Type, std::size_t... Index> +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) { +return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...); +} + +template<typename> +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { +return true; +} + +template<typename Type> +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) { +if constexpr(is_iterator_v<Type>) { +return true; +} else if constexpr(std::is_same_v<typename Type::value_type, Type>) { +return maybe_equality_comparable<Type>(choice<0>); +} else { +return is_equality_comparable<typename Type::value_type>::value; +} +} + +template<typename Type> +[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) { +if constexpr(has_tuple_size_value<Type>::value) { +return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{}); +} else { +return maybe_equality_comparable<Type>(choice<1>); +} +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +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>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template<typename Type> +inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +struct constness_as { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::remove_const_t<To>; +}; + +/*! @copydoc constness_as */ +template<typename To, typename From> +struct constness_as<To, const From> { +/*! @brief The type resulting from the transcription of the constness. */ +using type = std::add_const_t<To>; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template<typename To, typename From> +using constness_as_t = typename constness_as<To, From>::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +class member_class { +static_assert(std::is_member_pointer_v<Member>, "Invalid pointer type to non-static member object or function"); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...)); + +template<typename Class, typename Ret, typename... Args> +static Class *clazz(Ret (Class::*)(Args...) const); + +template<typename Class, typename Type> +static Class *clazz(Type Class::*); + +public: +/*! @brief The class of the given non-static member object or function. */ +using type = std::remove_pointer_t<decltype(clazz(std::declval<Member>()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template<typename Member> +using member_class_t = typename member_class<Member>::type; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Type, std::size_t, typename = void> +struct compressed_pair_element { +using reference = Type &; +using const_reference = const Type &; + +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>> +compressed_pair_element() +: value{} {} + +template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>> +compressed_pair_element(Args &&args) +: value{std::forward<Args>(args)} {} + +template<typename... Args, std::size_t... Index> +compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) +: value{std::forward<Args>(std::get<Index>(args))...} {} + +[[nodiscard]] reference get() ENTT_NOEXCEPT { +return value; +} + +[[nodiscard]] const_reference get() const ENTT_NOEXCEPT { +return value; +} + +private: +Type value; +}; + +template<typename Type, std::size_t Tag> +struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type { +using reference = Type &; +using const_reference = const Type &; +using base_type = Type; + +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>> +compressed_pair_element() +: base_type{} {} + +template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>> +compressed_pair_element(Args &&args) +: base_type{std::forward<Args>(args)} {} + +template<typename... Args, std::size_t... Index> +compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) +: base_type{std::forward<Args>(std::get<Index>(args))...} {} + +[[nodiscard]] reference get() ENTT_NOEXCEPT { +return *this; +} + +[[nodiscard]] const_reference get() const ENTT_NOEXCEPT { +return *this; +} +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +class compressed_pair final +: internal::compressed_pair_element<First, 0u>, +internal::compressed_pair_element<Second, 1u> { +using first_base = internal::compressed_pair_element<First, 0u>; +using second_base = internal::compressed_pair_element<Second, 1u>; + +public: +/*! @brief The type of the first element that the pair stores. */ +using first_type = First; +/*! @brief The type of the second element that the pair stores. */ +using second_type = Second; + +/** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ +template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>> +constexpr compressed_pair() +: first_base{}, +second_base{} {} + +/** + * @brief Copy constructor. + * @param other The instance to copy from. + */ +constexpr compressed_pair(const compressed_pair &other) = default; + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +constexpr compressed_pair(compressed_pair &&other) = default; + +/** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ +template<typename Arg, typename Other> +constexpr compressed_pair(Arg &&arg, Other &&other) +: first_base{std::forward<Arg>(arg)}, +second_base{std::forward<Other>(other)} {} + +/** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ +template<typename... Args, typename... Other> +constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) +: first_base{std::move(args), std::index_sequence_for<Args...>{}}, +second_base{std::move(other), std::index_sequence_for<Other...>{}} {} + +/** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ +constexpr compressed_pair &operator=(const compressed_pair &other) = default; + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ +constexpr compressed_pair &operator=(compressed_pair &&other) = default; + +/** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ +[[nodiscard]] first_type &first() ENTT_NOEXCEPT { +return static_cast<first_base &>(*this).get(); +} + +/*! @copydoc first */ +[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { +return static_cast<const first_base &>(*this).get(); +} + +/** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ +[[nodiscard]] second_type &second() ENTT_NOEXCEPT { +return static_cast<second_base &>(*this).get(); +} + +/*! @copydoc second */ +[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { +return static_cast<const second_base &>(*this).get(); +} + +/** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ +void swap(compressed_pair &other) { +using std::swap; +swap(first(), other.first()); +swap(second(), other.second()); +} + +/** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ +template<std::size_t Index> +decltype(auto) get() ENTT_NOEXCEPT { +if constexpr(Index == 0u) { +return first(); +} else { +static_assert(Index == 1u, "Index out of bounds"); +return second(); +} +} + +/*! @copydoc get */ +template<std::size_t Index> +decltype(auto) get() const ENTT_NOEXCEPT { +if constexpr(Index == 0u) { +return first(); +} else { +static_assert(Index == 1u, "Index out of bounds"); +return second(); +} +} +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template<typename Type, typename Other> +compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template<typename First, typename Second> +inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) { +lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<typename First, typename Second> +struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template<size_t Index, typename First, typename Second> +struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> { +static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include <cstdint> +#include <type_traits> +// #include "../config/config.h" + + +namespace entt { + +template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)> +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include <string_view> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#endif + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include <cstddef> +#include <cstdint> +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename> +struct fnv1a_traits; + +template<> +struct fnv1a_traits<std::uint32_t> { +using type = std::uint32_t; +static constexpr std::uint32_t offset = 2166136261; +static constexpr std::uint32_t prime = 16777619; +}; + +template<> +struct fnv1a_traits<std::uint64_t> { +using type = std::uint64_t; +static constexpr std::uint64_t offset = 14695981039346656037ull; +static constexpr std::uint64_t prime = 1099511628211ull; +}; + +template<typename Char> +struct basic_hashed_string { +using value_type = Char; +using size_type = std::size_t; +using hash_type = id_type; + +const value_type *repr; +size_type length; +hash_type hash; +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers in the codebase while using their numeric + * counterparts at runtime.<br/> + * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +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>; + +struct const_wrapper { +// non-explicit constructor on purpose +constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {} +const Char *repr; +}; + +// Fowler–Noll–Vo hash function v. 1a - the good +[[nodiscard]] static constexpr auto helper(const Char *str) ENTT_NOEXCEPT { +base_type base{str, 0u, hs_traits::offset}; + +for(; str[base.length]; ++base.length) { +base.hash = (base.hash ^ static_cast<hs_traits::type>(str[base.length])) * hs_traits::prime; +} + +return base; +} + +// Fowler–Noll–Vo hash function v. 1a - the good +[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) ENTT_NOEXCEPT { +base_type base{str, len, hs_traits::offset}; + +for(size_type pos{}; pos < len; ++pos) { +base.hash = (base.hash ^ static_cast<hs_traits::type>(str[pos])) * hs_traits::prime; +} + +return base; +} + +public: +/*! @brief Character type. */ +using value_type = typename base_type::value_type; +/*! @brief Unsigned integer type. */ +using size_type = typename base_type::size_type; +/*! @brief Unsigned integer type. */ +using hash_type = typename base_type::hash_type; + +/** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ +[[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) ENTT_NOEXCEPT { +return basic_hashed_string{str, len}; +} + +/** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ +template<std::size_t N> +[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { +return basic_hashed_string{str}; +} + +/** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ +[[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { +return basic_hashed_string{wrapper}; +} + +/*! @brief Constructs an empty hashed string. */ +constexpr basic_hashed_string() ENTT_NOEXCEPT +: base_type{} {} + +/** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +constexpr basic_hashed_string(const value_type *str, const size_type len) ENTT_NOEXCEPT +: base_type{helper(str, len)} {} + +/** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template<std::size_t N> +constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT +: base_type{helper(str)} {} + +/** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ +explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT +: base_type{helper(wrapper.repr)} {} + +/** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ +[[nodiscard]] constexpr size_type size() const ENTT_NOEXCEPT { +return base_type::length; +} + +/** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ +[[nodiscard]] constexpr const value_type *data() const ENTT_NOEXCEPT { +return base_type::repr; +} + +/** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ +[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { +return base_type::hash; +} + +/*! @copydoc data */ +[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { +return data(); +} + +/** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ +[[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template<typename Char> +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template<typename Char, std::size_t N> +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>; + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template<typename Char> +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string<char>; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string<wchar_t>; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT { +return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { +return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +struct ENTT_API type_index final { +[[nodiscard]] static id_type next() ENTT_NOEXCEPT { +static ENTT_MAYBE_ATOMIC(id_type) value{}; +return value++; +} +}; + +template<typename Type> +[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION +std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; +auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); +auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); +return value; +#else +return std::string_view{""}; +#endif +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { +constexpr auto value = stripped_type_name<Type>(); +return value; +} + +template<typename Type> +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { +static const auto value = stripped_type_name<Type>(); +return value; +} + +template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { +constexpr auto stripped = stripped_type_name<Type>(); +constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); +return value; +} + +template<typename Type> +[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { +static const auto value = [](const auto stripped) { +return hashed_string::value(stripped.data(), stripped.size()); +}(stripped_type_name<Type>()); +return value; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template<typename Type, typename = void> +struct ENTT_API type_index final { +/** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ +[[nodiscard]] static id_type value() ENTT_NOEXCEPT { +static const id_type value = internal::type_index::next(); +return value; +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template<typename Type, typename = void> +struct type_hash final { +/** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION +[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { +return internal::type_hash<Type>(0); +#else +[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { +return type_index<Type>::value(); +#endif +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { +return value(); +} +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template<typename Type, typename = void> +struct type_name final { +/** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ +[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { +return internal::type_name<Type>(0); +} + +/*! @copydoc value */ +[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { +return value(); +} +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { +/** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ +template<typename Type> +constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT +: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, +identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()}, +alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {} + +/** + * @brief Type index. + * @return Type index. + */ +[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT { +return seq; +} + +/** + * @brief Type hash. + * @return Type hash. + */ +[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { +return identifier; +} + +/** + * @brief Type name. + * @return Type name. + */ +[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT { +return alias; +} + +private: +id_type seq; +id_type identifier; +std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { +return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.<br/> + * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template<typename Type> +[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT { +if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) { +static type_info instance{std::in_place_type<Type>}; +return instance; +} else { +return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} +} + +/*! @copydoc type_id */ +template<typename Type> +[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT { +return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>(); +} + +} // namespace entt + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include <utility> +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { +/*! @brief Indicates that this is a transparent function object. */ +using is_transparent = void; + +/** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ +template<class Type> +[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { +return std::forward<Type>(value); +} +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template<typename Type, typename Class> +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template<typename Func> +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template<class... Func> +struct overloaded: Func... { +using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template<class... Func> +overloaded(Func...) -> overloaded<Func...>; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template<class Func> +struct y_combinator { +/** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ +y_combinator(Func recursive) +: func{std::move(recursive)} {} + +/** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ +template<class... Args> +decltype(auto) operator()(Args &&...args) const { +return func(*this, std::forward<Args>(args)...); +} + +/*! @copydoc operator()() */ +template<class... Args> +decltype(auto) operator()(Args &&...args) { +return func(*this, std::forward<Args>(args)...); +} + +private: +Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + +#include <algorithm> +#include <functional> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" + +// #include "delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + +#include <cstddef> +#include <functional> +#include <tuple> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template<typename Ret, typename... Args> +auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template<typename Ret, typename Type, typename... Args, typename Other> +auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template<typename Class, typename Ret, typename... Args, typename... Other> +auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template<typename Class, typename Ret, typename... Args, typename... Other> +auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template<typename Class, typename Type, typename... Other> +auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template<typename... Type> +using function_pointer_t = decltype(internal::function_pointer(std::declval<Type>()...)); + +template<typename... Class, typename Ret, typename... Args> +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { +return std::index_sequence_for<Class..., Args...>{}; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @brief Used to wrap a function or a member of a specified type. */ +template<auto> +struct connect_arg_t {}; + +/*! @brief Constant of type connect_arg_t used to disambiguate calls. */ +template<auto Func> +inline constexpr connect_arg_t<Func> connect_arg{}; + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template<typename> +class delegate; + +/** + * @brief Utility class to use to send around functions and members. + * + * Unmanaged delegate for function pointers and members. Users of this class are + * in charge of disconnecting instances before deleting them. + * + * A delegate can be used as a general purpose invoker without memory overhead + * for free functions possibly with payloads and bound or unbound members. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template<typename Ret, typename... Args> +class delegate<Ret(Args...)> { +template<auto Candidate, std::size_t... Index> +[[nodiscard]] auto wrap(std::index_sequence<Index...>) ENTT_NOEXCEPT { +return [](const void *, Args... args) -> Ret { +[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...); +return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...)); +}; +} + +template<auto Candidate, typename Type, std::size_t... Index> +[[nodiscard]] auto wrap(Type &, std::index_sequence<Index...>) ENTT_NOEXCEPT { +return [](const void *payload, Args... args) -> Ret { +[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...); +Type *curr = static_cast<Type *>(const_cast<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))...)); +}; +} + +template<auto Candidate, typename Type, std::size_t... Index> +[[nodiscard]] auto wrap(Type *, std::index_sequence<Index...>) ENTT_NOEXCEPT { +return [](const void *payload, Args... args) -> Ret { +[[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...); +Type *curr = static_cast<Type *>(const_cast<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))...)); +}; +} + +public: +/*! @brief Function type of the contained target. */ +using function_type = Ret(const void *, Args...); +/*! @brief Function type of the delegate. */ +using type = Ret(Args...); +/*! @brief Return type of the delegate. */ +using result_type = Ret; + +/*! @brief Default constructor. */ +delegate() ENTT_NOEXCEPT +: instance{nullptr}, +fn{nullptr} {} + +/** + * @brief Constructs a delegate and connects a free function or an unbound + * member. + * @tparam Candidate Function or member to connect to the delegate. + */ +template<auto Candidate> +delegate(connect_arg_t<Candidate>) ENTT_NOEXCEPT { +connect<Candidate>(); +} + +/** + * @brief Constructs a delegate and connects a free function with payload or + * a bound member. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ +template<auto Candidate, typename Type> +delegate(connect_arg_t<Candidate>, Type &&value_or_instance) ENTT_NOEXCEPT { +connect<Candidate>(std::forward<Type>(value_or_instance)); +} + +/** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ +delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { +connect(function, payload); +} + +/** + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. + */ +template<auto Candidate> +void connect() ENTT_NOEXCEPT { +instance = nullptr; + +if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) { +fn = [](const void *, Args... args) -> Ret { +return Ret(std::invoke(Candidate, std::forward<Args>(args)...)); +}; +} else if constexpr(std::is_member_pointer_v<decltype(Candidate)>) { +fn = wrap<Candidate>(internal::index_sequence_for<type_list_element_t<0, type_list<Args...>>>(internal::function_pointer_t<decltype(Candidate)>{})); +} else { +fn = wrap<Candidate>(internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate)>{})); +} +} + +/** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the delegate.<br/> + * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the delegate itself. + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid reference that fits the purpose. + */ +template<auto Candidate, typename Type> +void connect(Type &value_or_instance) ENTT_NOEXCEPT { +instance = &value_or_instance; + +if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, Args...>) { +fn = [](const void *payload, Args... args) -> Ret { +Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload)); +return Ret(std::invoke(Candidate, *curr, std::forward<Args>(args)...)); +}; +} else { +fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{})); +} +} + +/** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * @sa connect(Type &) + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + */ +template<auto Candidate, typename Type> +void connect(Type *value_or_instance) ENTT_NOEXCEPT { +instance = value_or_instance; + +if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, Args...>) { +fn = [](const void *payload, Args... args) -> Ret { +Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload)); +return Ret(std::invoke(Candidate, curr, std::forward<Args>(args)...)); +}; +} else { +fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{})); +} +} + +/** + * @brief Connects an user defined function with optional payload to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of an instance overcomes + * the one of the delegate.<br/> + * The payload is returned as the first argument to the target function in + * all cases. + * + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ +void connect(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { +instance = payload; +fn = function; +} + +/** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ +void reset() ENTT_NOEXCEPT { +instance = nullptr; +fn = nullptr; +} + +/** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ +[[nodiscard]] const void *data() const ENTT_NOEXCEPT { +return instance; +} + +/** + * @brief Triggers a delegate. + * + * The delegate invokes the underlying function and returns the result. + * + * @warning + * Attempting to trigger an invalid delegate results in undefined + * behavior. + * + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. + */ +Ret operator()(Args... args) const { +ENTT_ASSERT(static_cast<bool>(*this), "Uninitialized delegate"); +return fn(instance, std::forward<Args>(args)...); +} + +/** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +// no need to also test instance +return !(fn == nullptr); +} + +/** + * @brief Compares the contents of two delegates. + * @param other Delegate with which to compare. + * @return False if the two contents differ, true otherwise. + */ +[[nodiscard]] bool operator==(const delegate<Ret(Args...)> &other) const ENTT_NOEXCEPT { +return fn == other.fn && instance == other.instance; +} + +private: +const void *instance; +function_type *fn; +}; + +/** + * @brief Compares the contents of two delegates. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two contents differ, false otherwise. + */ +template<typename Ret, typename... Args> +[[nodiscard]] bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)> &rhs) ENTT_NOEXCEPT { +return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template<auto Candidate> +delegate(connect_arg_t<Candidate>) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate)>>>; + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + */ +template<auto Candidate, typename Type> +delegate(connect_arg_t<Candidate>, Type &&) -> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate), Type>>>; + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template<typename Ret, typename... Args> +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate<Ret(Args...)>; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Sink class. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid signal handler type. + */ +template<typename Type> +class sink; + +/** + * @brief Unmanaged signal handler. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Type, typename Allocator> +class sigh; + +/** + * @brief Unmanaged signal handler. + * + * It works directly with references to classes and pointers to member functions + * as well as pointers to free functions. Users of this class are in charge of + * disconnecting instances before deleting them. + * + * This class serves mainly two purposes: + * + * * Creating signals to use later to notify a bunch of listeners. + * * Collecting results from a set of functions like in a voting system. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Ret, typename... Args, typename Allocator> +class sigh<Ret(Args...), Allocator> { +/*! @brief A sink is allowed to modify a signal. */ +friend class sink<sigh<Ret(Args...), Allocator>>; + +using alloc_traits = std::allocator_traits<Allocator>; +static_assert(std::is_same_v<typename alloc_traits::value_type, Ret (*)(Args...)>, "Invalid value type"); +using container_type = std::vector<delegate<Ret(Args...)>, typename alloc_traits::template rebind_alloc<delegate<Ret(Args...)>>>; + +public: +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Sink type. */ +using sink_type = sink<sigh<Ret(Args...), Allocator>>; + +/*! @brief Default constructor. */ +sigh() +: sigh{allocator_type{}} {} + +/** + * @brief Constructs a signal handler with a given allocator. + * @param allocator The allocator to use. + */ +explicit sigh(const allocator_type &allocator) +: calls{allocator} {} + +/** + * @brief Copy constructor. + * @param other The instance to copy from. + */ +sigh(const sigh &other) +: calls{other.calls} {} + +/** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ +sigh(const sigh &other, const allocator_type &allocator) +: calls{other.calls, allocator} {} + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +sigh(sigh &&other) ENTT_NOEXCEPT +: calls{std::move(other.calls)} {} + +/** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ +sigh(sigh &&other, const allocator_type &allocator) ENTT_NOEXCEPT +: calls{std::move(other.calls), allocator} {} + +/** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This signal handler. + */ +sigh &operator=(const sigh &other) { +calls = other.calls; +return *this; +} + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This signal handler. + */ +sigh &operator=(sigh &&other) ENTT_NOEXCEPT { +calls = std::move(other.calls); +return *this; +} + +/** + * @brief Exchanges the contents with those of a given signal handler. + * @param other Signal handler to exchange the content with. + */ +void swap(sigh &other) { +using std::swap; +swap(calls, other.calls); +} + +/** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ +[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { +return calls.get_allocator(); +} + +/** + * @brief Instance type when it comes to connecting member functions. + * @tparam Class Type of class to which the member function belongs. + */ +template<typename Class> +using instance_type = Class *; + +/** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return calls.size(); +} + +/** + * @brief Returns false if at least a listener is connected to the signal. + * @return True if the signal has no listeners connected, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return calls.empty(); +} + +/** + * @brief Triggers a signal. + * + * All the listeners are notified. Order isn't guaranteed. + * + * @param args Arguments to use to invoke listeners. + */ +void publish(Args... args) const { +for(auto &&call: std::as_const(calls)) { +call(args...); +} +} + +/** + * @brief Collects return values from the listeners. + * + * The collector must expose a call operator with the following properties: + * + * * The return type is either `void` or such that it's convertible to + * `bool`. In the second case, a true value will stop the iteration. + * * The list of parameters is empty if `Ret` is `void`, otherwise it + * contains a single element such that `Ret` is convertible to it. + * + * @tparam Func Type of collector to use, if any. + * @param func A valid function object. + * @param args Arguments to use to invoke listeners. + */ +template<typename Func> +void collect(Func func, Args... args) const { +for(auto &&call: calls) { +if constexpr(std::is_void_v<Ret>) { +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...))) { break; } +} else { +func(call(args...)); +} +} +} +} + +private: +container_type calls; +}; + +/** + * @brief Connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it. + */ +class connection { +/*! @brief A sink is allowed to create connection objects. */ +template<typename> +friend class sink; + +connection(delegate<void(void *)> fn, void *ref) +: disconnect{fn}, signal{ref} {} + +public: +/*! @brief Default constructor. */ +connection() = default; + +/** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(disconnect); +} + +/*! @brief Breaks the connection. */ +void release() { +if(disconnect) { +disconnect(signal); +disconnect.reset(); +} +} + +private: +delegate<void(void *)> disconnect; +void *signal{}; +}; + +/** + * @brief Scoped connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it.<br/> + * A scoped connection automatically breaks the link between the two objects + * when it goes out of scope. + */ +struct scoped_connection { +/*! @brief Default constructor. */ +scoped_connection() = default; + +/** + * @brief Constructs a scoped connection from a basic connection. + * @param other A valid connection object. + */ +scoped_connection(const connection &other) +: conn{other} {} + +/*! @brief Default copy constructor, deleted on purpose. */ +scoped_connection(const scoped_connection &) = delete; + +/** + * @brief Move constructor. + * @param other The scoped connection to move from. + */ +scoped_connection(scoped_connection &&other) ENTT_NOEXCEPT +: conn{std::exchange(other.conn, {})} {} + +/*! @brief Automatically breaks the link on destruction. */ +~scoped_connection() { +conn.release(); +} + +/** + * @brief Default copy assignment operator, deleted on purpose. + * @return This scoped connection. + */ +scoped_connection &operator=(const scoped_connection &) = delete; + +/** + * @brief Move assignment operator. + * @param other The scoped connection to move from. + * @return This scoped connection. + */ +scoped_connection &operator=(scoped_connection &&other) ENTT_NOEXCEPT { +conn = std::exchange(other.conn, {}); +return *this; +} + +/** + * @brief Acquires a connection. + * @param other The connection object to acquire. + * @return This scoped connection. + */ +scoped_connection &operator=(connection other) { +conn = std::move(other); +return *this; +} + +/** + * @brief Checks whether a scoped connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(conn); +} + +/*! @brief Breaks the connection. */ +void release() { +conn.release(); +} + +private: +connection conn; +}; + +/** + * @brief Sink class. + * + * A sink is used to connect listeners to signals and to disconnect them.<br/> + * The function type for a listener is the one of the signal to which it + * belongs. + * + * The clear separation between a signal and a sink permits to store the former + * as private data member without exposing the publish functionality to the + * users of the class. + * + * @warning + * Lifetime of a sink must not overcome that of the signal to which it refers. + * In any other case, attempting to use a sink results in undefined behavior. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Ret, typename... Args, typename Allocator> +class sink<sigh<Ret(Args...), Allocator>> { +using signal_type = sigh<Ret(Args...), Allocator>; +using difference_type = typename signal_type::container_type::difference_type; + +template<auto Candidate, typename Type> +static void release(Type value_or_instance, void *signal) { +sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>(value_or_instance); +} + +template<auto Candidate> +static void release(void *signal) { +sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>(); +} + +public: +/** + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. + */ +sink(sigh<Ret(Args...), Allocator> &ref) ENTT_NOEXCEPT +: offset{}, +signal{&ref} {} + +/** + * @brief Returns false if at least a listener is connected to the sink. + * @return True if the sink has no listeners connected, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +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>(); + +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; +} + +/** + * @brief Returns a sink that connects before a free function with payload + * or a bound member. + * @tparam Candidate Member or free function to look for. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ +template<auto Candidate, typename Type> +[[nodiscard]] sink before(Type &&value_or_instance) { +delegate<Ret(Args...)> call{}; +call.template connect<Candidate>(value_or_instance); + +const auto &calls = signal->calls; +const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + +sink other{*this}; +other.offset = calls.cend() - it; +return other; +} + +/** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ +template<typename Type> +[[nodiscard]] sink before(Type &value_or_instance) { +return before(&value_or_instance); +} + +/** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + * @return A properly initialized sink object. + */ +template<typename Type> +[[nodiscard]] sink before(Type *value_or_instance) { +sink other{*this}; + +if(value_or_instance) { +const auto &calls = signal->calls; +const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) { +return delegate.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 or an unbound member to a signal. + * + * The signal handler performs checks to avoid multiple connections for the + * same function. + * + * @tparam Candidate Function or member to connect to the signal. + * @return A properly initialized connection object. + */ +template<auto Candidate> +connection connect() { +disconnect<Candidate>(); + +delegate<Ret(Args...)> call{}; +call.template connect<Candidate>(); +signal->calls.insert(signal->calls.end() - offset, std::move(call)); + +delegate<void(void *)> conn{}; +conn.template connect<&release<Candidate>>(); +return {std::move(conn), signal}; +} + +/** + * @brief Connects a free function with payload or a bound member to a + * signal. + * + * The signal isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the 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. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized connection object. + */ +template<auto Candidate, typename Type> +connection connect(Type &&value_or_instance) { +disconnect<Candidate>(value_or_instance); + +delegate<Ret(Args...)> call{}; +call.template connect<Candidate>(value_or_instance); +signal->calls.insert(signal->calls.end() - offset, std::move(call)); + +delegate<void(void *)> conn{}; +conn.template connect<&release<Candidate, Type>>(value_or_instance); +return {std::move(conn), signal}; +} + +/** + * @brief Disconnects a free function or an unbound member from a signal. + * @tparam Candidate Function or member to disconnect from the signal. + */ +template<auto Candidate> +void disconnect() { +auto &calls = signal->calls; +delegate<Ret(Args...)> call{}; +call.template connect<Candidate>(); +calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); +} + +/** + * @brief Disconnects a free function with payload or a bound member from a + * signal. + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ +template<auto Candidate, typename Type> +void disconnect(Type &&value_or_instance) { +auto &calls = signal->calls; +delegate<Ret(Args...)> call{}; +call.template connect<Candidate>(value_or_instance); +calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); +} + +/** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ +template<typename Type> +void disconnect(Type &value_or_instance) { +disconnect(&value_or_instance); +} + +/** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ +template<typename Type> +void disconnect(Type *value_or_instance) { +if(value_or_instance) { +auto &calls = signal->calls; +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()); +} +} + +/*! @brief Disconnects all the listeners from a signal. */ +void disconnect() { +signal->calls.clear(); +} + +private: +difference_type offset; +signal_type *signal; +}; + +/** + * @brief Deduction guide. + * + * It allows to deduce the signal handler type of a sink directly from the + * signal it refers to. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Ret, typename... Args, typename Allocator> +sink(sigh<Ret(Args...), Allocator> &) -> sink<sigh<Ret(Args...), Allocator>>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +struct basic_dispatcher_handler { +virtual ~basic_dispatcher_handler() = default; +virtual void publish() = 0; +virtual void disconnect(void *) = 0; +virtual void clear() ENTT_NOEXCEPT = 0; +virtual std::size_t size() const ENTT_NOEXCEPT = 0; +}; + +template<typename Event, typename Allocator> +class dispatcher_handler final: public basic_dispatcher_handler { +static_assert(std::is_same_v<Event, std::decay_t<Event>>, "Invalid event type"); + +using alloc_traits = std::allocator_traits<Allocator>; +using signal_type = sigh<void(Event &), typename alloc_traits::template rebind_alloc<void (*)(Event &)>>; +using container_type = std::vector<Event, typename alloc_traits::template rebind_alloc<Event>>; + +public: +using allocator_type = Allocator; + +dispatcher_handler(const allocator_type &allocator) +: signal{allocator}, +events{allocator} {} + +void publish() override { +const auto length = events.size(); + +for(std::size_t pos{}; pos < length; ++pos) { +signal.publish(events[pos]); +} + +events.erase(events.cbegin(), events.cbegin() + length); +} + +void disconnect(void *instance) override { +bucket().disconnect(instance); +} + +void clear() ENTT_NOEXCEPT override { +events.clear(); +} + +[[nodiscard]] auto bucket() ENTT_NOEXCEPT { +using sink_type = typename sigh<void(Event &)>::sink_type; +return sink_type{signal}; +} + +void trigger(Event event) { +signal.publish(event); +} + +template<typename... Args> +void enqueue(Args &&...args) { +if constexpr(std::is_aggregate_v<Event>) { +events.push_back(Event{std::forward<Args>(args)...}); +} else { +events.emplace_back(std::forward<Args>(args)...); +} +} + +std::size_t size() const ENTT_NOEXCEPT override { +return events.size(); +} + +private: +signal_type signal; +container_type events; +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Basic dispatcher implementation. + * + * A dispatcher can be used either to trigger an immediate event or to enqueue + * events to be published all together once per tick.<br/> + * Listeners are provided in the form of member functions. For each event of + * type `Event`, listeners are such that they can be invoked with an argument of + * type `Event &`, no matter what the return type is. + * + * The dispatcher creates instances of the `sigh` class internally. Refer to the + * documentation of the latter for more details. + * + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Allocator> +class basic_dispatcher { +template<typename Event> +using handler_type = internal::dispatcher_handler<Event, Allocator>; + +using key_type = id_type; +// std::shared_ptr because of its type erased allocator which is pretty useful here +using mapped_type = std::shared_ptr<internal::basic_dispatcher_handler>; + +using alloc_traits = std::allocator_traits<Allocator>; +using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const key_type, mapped_type>>; +using container_type = dense_map<id_type, mapped_type, identity, std::equal_to<id_type>, container_allocator>; + +template<typename Event> +[[nodiscard]] handler_type<Event> &assure(const id_type id) { +auto &&ptr = pools.first()[id]; + +if(!ptr) { +const auto &allocator = pools.second(); +ptr = std::allocate_shared<handler_type<Event>>(allocator, allocator); +} + +return static_cast<handler_type<Event> &>(*ptr); +} + +template<typename Event> +[[nodiscard]] const handler_type<Event> *assure(const id_type id) const { +auto &container = pools.first(); + +if(const auto it = container.find(id); it != container.end()) { +return static_cast<const handler_type<Event> *>(it->second.get()); +} + +return nullptr; +} + +public: +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; + +/*! @brief Default constructor. */ +basic_dispatcher() +: basic_dispatcher{allocator_type{}} {} + +/** + * @brief Constructs a dispatcher with a given allocator. + * @param allocator The allocator to use. + */ +explicit basic_dispatcher(const allocator_type &allocator) +: pools{allocator, allocator} {} + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +basic_dispatcher(basic_dispatcher &&other) ENTT_NOEXCEPT +: pools{std::move(other.pools)} {} + +/** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ +basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) ENTT_NOEXCEPT +: pools{container_type{std::move(other.pools.first()), allocator}, allocator} {} + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This dispatcher. + */ +basic_dispatcher &operator=(basic_dispatcher &&other) ENTT_NOEXCEPT { +pools = std::move(other.pools); +return *this; +} + +/** + * @brief Exchanges the contents with those of a given dispatcher. + * @param other Dispatcher to exchange the content with. + */ +void swap(basic_dispatcher &other) { +using std::swap; +swap(pools, other.pools); +} + +/** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ +[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { +return pools.second(); +} + +/** + * @brief Returns the number of pending events for a given type. + * @tparam Event Type of event for which to return the count. + * @param id Name used to map the event queue within the dispatcher. + * @return The number of pending events for the given type. + */ +template<typename Event> +size_type size(const id_type id = type_hash<Event>::value()) const ENTT_NOEXCEPT { +const auto *cpool = assure<Event>(id); +return cpool ? cpool->size() : 0u; +} + +/** + * @brief Returns the total number of pending events. + * @return The total number of pending events. + */ +size_type size() const ENTT_NOEXCEPT { +size_type count{}; + +for(auto &&cpool: pools.first()) { +count += cpool.second->size(); +} + +return count; +} + +/** + * @brief Returns a sink object for the given event and queue. + * + * A sink is an opaque object used to connect listeners to events. + * + * The function type for a listener is _compatible_ with: + * @code{.cpp} + * void(Event &); + * @endcode + * + * The order of invocation of the listeners isn't guaranteed. + * + * @sa sink + * + * @tparam Event Type of event of which to get the sink. + * @param id Name used to map the event queue within the dispatcher. + * @return A temporary sink object. + */ +template<typename Event> +[[nodiscard]] auto sink(const id_type id = type_hash<Event>::value()) { +return assure<Event>(id).bucket(); +} + +/** + * @brief Triggers an immediate event of a given type. + * @tparam Event Type of event to trigger. + * @param event An instance of the given type of event. + */ +template<typename Event> +void trigger(Event &&event = {}) { +trigger(type_hash<std::decay_t<Event>>::value(), std::forward<Event>(event)); +} + +/** + * @brief Triggers an immediate event on a queue of a given type. + * @tparam Event Type of event to trigger. + * @param event An instance of the given type of event. + * @param id Name used to map the event queue within the dispatcher. + */ +template<typename Event> +void trigger(const id_type id, Event &&event = {}) { +assure<std::decay_t<Event>>(id).trigger(std::forward<Event>(event)); +} + +/** + * @brief Enqueues an event of the given type. + * @tparam Event Type of event to enqueue. + * @tparam Args Types of arguments to use to construct the event. + * @param args Arguments to use to construct the event. + */ +template<typename Event, typename... Args> +void enqueue(Args &&...args) { +enqueue_hint<Event>(type_hash<Event>::value(), std::forward<Args>(args)...); +} + +/** + * @brief Enqueues an event of the given type. + * @tparam Event Type of event to enqueue. + * @param event An instance of the given type of event. + */ +template<typename Event> +void enqueue(Event &&event) { +enqueue_hint(type_hash<std::decay_t<Event>>::value(), std::forward<Event>(event)); +} + +/** + * @brief Enqueues an event of the given type. + * @tparam Event Type of event to enqueue. + * @tparam Args Types of arguments to use to construct the event. + * @param id Name used to map the event queue within the dispatcher. + * @param args Arguments to use to construct the event. + */ +template<typename Event, typename... Args> +void enqueue_hint(const id_type id, Args &&...args) { +assure<Event>(id).enqueue(std::forward<Args>(args)...); +} + +/** + * @brief Enqueues an event of the given type. + * @tparam Event Type of event to enqueue. + * @param id Name used to map the event queue within the dispatcher. + * @param event An instance of the given type of event. + */ +template<typename Event> +void enqueue_hint(const id_type id, Event &&event) { +assure<std::decay_t<Event>>(id).enqueue(std::forward<Event>(event)); +} + +/** + * @brief Utility function to disconnect everything related to a given value + * or instance from a dispatcher. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ +template<typename Type> +void disconnect(Type &value_or_instance) { +disconnect(&value_or_instance); +} + +/** + * @brief Utility function to disconnect everything related to a given value + * or instance from a dispatcher. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ +template<typename Type> +void disconnect(Type *value_or_instance) { +for(auto &&cpool: pools.first()) { +cpool.second->disconnect(value_or_instance); +} +} + +/** + * @brief Discards all the events stored so far in a given queue. + * @tparam Event Type of event to discard. + * @param id Name used to map the event queue within the dispatcher. + */ +template<typename Event> +void clear(const id_type id = type_hash<Event>::value()) { +assure<Event>(id).clear(); +} + +/*! @brief Discards all the events queued so far. */ +void clear() ENTT_NOEXCEPT { +for(auto &&cpool: pools.first()) { +cpool.second->clear(); +} +} + +/** + * @brief Delivers all the pending events of a given queue. + * @tparam Event Type of event to send. + * @param id Name used to map the event queue within the dispatcher. + */ +template<typename Event> +void update(const id_type id = type_hash<Event>::value()) { +assure<Event>(id).publish(); +} + +/*! @brief Delivers all the pending events. */ +void update() const { +for(auto &&cpool: pools.first()) { +cpool.second->publish(); +} +} + +private: +compressed_pair<container_type, allocator_type> pools; +}; + +} // namespace entt + +#endif + +// #include "signal/emitter.hpp" +#ifndef ENTT_SIGNAL_EMITTER_HPP +#define ENTT_SIGNAL_EMITTER_HPP + +#include <algorithm> +#include <functional> +#include <iterator> +#include <list> +#include <memory> +#include <type_traits> +#include <utility> +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/utility.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief General purpose event emitter. + * + * The emitter class template follows the CRTP idiom. To create a custom emitter + * type, derived classes must inherit directly from the base class as: + * + * @code{.cpp} + * struct my_emitter: emitter<my_emitter> { + * // ... + * } + * @endcode + * + * Pools for the type of events are created internally on the fly. It's not + * required to specify in advance the full list of accepted types.<br/> + * Moreover, whenever an event is published, an emitter provides the listeners + * with a reference to itself along with a reference to the event. Therefore + * listeners have an handy way to work with it without incurring in the need of + * capturing a reference to the emitter. + * + * @tparam Derived Actual type of emitter that extends the class template. + */ +template<typename Derived> +class emitter { +struct basic_pool { +virtual ~basic_pool() = default; +virtual bool empty() const ENTT_NOEXCEPT = 0; +virtual void clear() ENTT_NOEXCEPT = 0; +}; + +template<typename Event> +struct pool_handler final: basic_pool { +static_assert(std::is_same_v<Event, std::decay_t<Event>>, "Invalid event type"); + +using listener_type = std::function<void(Event &, Derived &)>; +using element_type = std::pair<bool, listener_type>; +using container_type = std::list<element_type>; +using connection_type = typename container_type::iterator; + +[[nodiscard]] bool empty() const ENTT_NOEXCEPT override { +auto pred = [](auto &&element) { return element.first; }; + +return std::all_of(once_list.cbegin(), once_list.cend(), pred) +&& std::all_of(on_list.cbegin(), on_list.cend(), pred); +} + +void clear() ENTT_NOEXCEPT override { +if(publishing) { +for(auto &&element: once_list) { +element.first = true; +} + +for(auto &&element: on_list) { +element.first = true; +} +} else { +once_list.clear(); +on_list.clear(); +} +} + +connection_type once(listener_type listener) { +return once_list.emplace(once_list.cend(), false, std::move(listener)); +} + +connection_type on(listener_type listener) { +return on_list.emplace(on_list.cend(), false, std::move(listener)); +} + +void erase(connection_type conn) { +conn->first = true; + +if(!publishing) { +auto pred = [](auto &&element) { return element.first; }; +once_list.remove_if(pred); +on_list.remove_if(pred); +} +} + +void publish(Event &event, Derived &ref) { +container_type swap_list; +once_list.swap(swap_list); + +publishing = true; + +for(auto &&element: on_list) { +element.first ? void() : element.second(event, ref); +} + +for(auto &&element: swap_list) { +element.first ? void() : element.second(event, ref); +} + +publishing = false; + +on_list.remove_if([](auto &&element) { return element.first; }); +} + +private: +bool publishing{false}; +container_type once_list{}; +container_type on_list{}; +}; + +template<typename Event> +[[nodiscard]] pool_handler<Event> *assure() { +if(auto &&ptr = pools[type_hash<Event>::value()]; !ptr) { +auto *cpool = new pool_handler<Event>{}; +ptr.reset(cpool); +return cpool; +} else { +return static_cast<pool_handler<Event> *>(ptr.get()); +} +} + +template<typename Event> +[[nodiscard]] const pool_handler<Event> *assure() const { +const auto it = pools.find(type_hash<Event>::value()); +return (it == pools.cend()) ? nullptr : static_cast<const pool_handler<Event> *>(it->second.get()); +} + +public: +/** @brief Type of listeners accepted for the given event. */ +template<typename Event> +using listener = typename pool_handler<Event>::listener_type; + +/** + * @brief Generic connection type for events. + * + * Type of the connection object returned by the event emitter whenever a + * listener for the given type is registered.<br/> + * It can be used to break connections still in use. + * + * @tparam Event Type of event for which the connection is created. + */ +template<typename Event> +struct connection: private pool_handler<Event>::connection_type { +/** @brief Event emitters are friend classes of connections. */ +friend class emitter; + +/*! @brief Default constructor. */ +connection() ENTT_NOEXCEPT = default; + +/** + * @brief Creates a connection that wraps its underlying instance. + * @param conn A connection object to wrap. + */ +connection(typename pool_handler<Event>::connection_type conn) +: pool_handler<Event>::connection_type{std::move(conn)} {} +}; + +/*! @brief Default constructor. */ +emitter() = default; + +/*! @brief Default destructor. */ +virtual ~emitter() ENTT_NOEXCEPT { +static_assert(std::is_base_of_v<emitter<Derived>, Derived>, "Incorrect use of the class template"); +} + +/*! @brief Default move constructor. */ +emitter(emitter &&) = default; + +/*! @brief Default move assignment operator. @return This emitter. */ +emitter &operator=(emitter &&) = default; + +/** + * @brief Emits the given event. + * + * All the listeners registered for the specific event type are invoked with + * the given event. The event type must either have a proper constructor for + * the arguments provided or be an aggregate type. + * + * @tparam Event Type of event to publish. + * @tparam Args Types of arguments to use to construct the event. + * @param args Parameters to use to initialize the event. + */ +template<typename Event, typename... Args> +void publish(Args &&...args) { +Event instance{std::forward<Args>(args)...}; +assure<Event>()->publish(instance, *static_cast<Derived *>(this)); +} + +/** + * @brief Registers a long-lived listener with the event emitter. + * + * This method can be used to register a listener designed to be invoked + * more than once for the given event type.<br/> + * The connection returned by the method can be freely discarded. It's meant + * to be used later to disconnect the listener if required. + * + * The listener is as a callable object that can be moved and the type of + * which is _compatible_ with `void(Event &, Derived &)`. + * + * @note + * Whenever an event is emitted, the emitter provides the listener with a + * reference to the derived class. Listeners don't have to capture those + * instances for later uses. + * + * @tparam Event Type of event to which to connect the listener. + * @param instance The listener to register. + * @return Connection object that can be used to disconnect the listener. + */ +template<typename Event> +connection<Event> on(listener<Event> instance) { +return assure<Event>()->on(std::move(instance)); +} + +/** + * @brief Registers a short-lived listener with the event emitter. + * + * This method can be used to register a listener designed to be invoked + * only once for the given event type.<br/> + * The connection returned by the method can be freely discarded. It's meant + * to be used later to disconnect the listener if required. + * + * The listener is as a callable object that can be moved and the type of + * which is _compatible_ with `void(Event &, Derived &)`. + * + * @note + * Whenever an event is emitted, the emitter provides the listener with a + * reference to the derived class. Listeners don't have to capture those + * instances for later uses. + * + * @tparam Event Type of event to which to connect the listener. + * @param instance The listener to register. + * @return Connection object that can be used to disconnect the listener. + */ +template<typename Event> +connection<Event> once(listener<Event> instance) { +return assure<Event>()->once(std::move(instance)); +} + +/** + * @brief Disconnects a listener from the event emitter. + * + * Do not use twice the same connection to disconnect a listener, it results + * in undefined behavior. Once used, discard the connection object. + * + * @tparam Event Type of event of the connection. + * @param conn A valid connection. + */ +template<typename Event> +void erase(connection<Event> conn) { +assure<Event>()->erase(std::move(conn)); +} + +/** + * @brief Disconnects all the listeners for the given event type. + * + * All the connections previously returned for the given event are + * invalidated. Using them results in undefined behavior. + * + * @tparam Event Type of event to reset. + */ +template<typename Event> +void clear() { +assure<Event>()->clear(); +} + +/** + * @brief Disconnects all the listeners. + * + * All the connections previously returned are invalidated. Using them + * results in undefined behavior. + */ +void clear() ENTT_NOEXCEPT { +for(auto &&cpool: pools) { +cpool.second->clear(); +} +} + +/** + * @brief Checks if there are listeners registered for the specific event. + * @tparam Event Type of event to test. + * @return True if there are no listeners registered, false otherwise. + */ +template<typename Event> +[[nodiscard]] bool empty() const { +const auto *cpool = assure<Event>(); +return !cpool || cpool->empty(); +} + +/** + * @brief Checks if there are listeners registered with the event emitter. + * @return True if there are no listeners registered, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return std::all_of(pools.cbegin(), pools.cend(), [](auto &&cpool) { +return cpool.second->empty(); +}); +} + +private: +dense_map<id_type, std::unique_ptr<basic_pool>, identity> pools{}; +}; + +} // namespace entt + +#endif + +// #include "signal/sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + +#include <algorithm> +#include <functional> +#include <type_traits> +#include <utility> +#include <vector> +// #include "../config/config.h" + +// #include "delegate.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Sink class. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid signal handler type. + */ +template<typename Type> +class sink; + +/** + * @brief Unmanaged signal handler. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Type, typename Allocator> +class sigh; + +/** + * @brief Unmanaged signal handler. + * + * It works directly with references to classes and pointers to member functions + * as well as pointers to free functions. Users of this class are in charge of + * disconnecting instances before deleting them. + * + * This class serves mainly two purposes: + * + * * Creating signals to use later to notify a bunch of listeners. + * * Collecting results from a set of functions like in a voting system. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Ret, typename... Args, typename Allocator> +class sigh<Ret(Args...), Allocator> { +/*! @brief A sink is allowed to modify a signal. */ +friend class sink<sigh<Ret(Args...), Allocator>>; + +using alloc_traits = std::allocator_traits<Allocator>; +static_assert(std::is_same_v<typename alloc_traits::value_type, Ret (*)(Args...)>, "Invalid value type"); +using container_type = std::vector<delegate<Ret(Args...)>, typename alloc_traits::template rebind_alloc<delegate<Ret(Args...)>>>; + +public: +/*! @brief Allocator type. */ +using allocator_type = Allocator; +/*! @brief Unsigned integer type. */ +using size_type = std::size_t; +/*! @brief Sink type. */ +using sink_type = sink<sigh<Ret(Args...), Allocator>>; + +/*! @brief Default constructor. */ +sigh() +: sigh{allocator_type{}} {} + +/** + * @brief Constructs a signal handler with a given allocator. + * @param allocator The allocator to use. + */ +explicit sigh(const allocator_type &allocator) +: calls{allocator} {} + +/** + * @brief Copy constructor. + * @param other The instance to copy from. + */ +sigh(const sigh &other) +: calls{other.calls} {} + +/** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ +sigh(const sigh &other, const allocator_type &allocator) +: calls{other.calls, allocator} {} + +/** + * @brief Move constructor. + * @param other The instance to move from. + */ +sigh(sigh &&other) ENTT_NOEXCEPT +: calls{std::move(other.calls)} {} + +/** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ +sigh(sigh &&other, const allocator_type &allocator) ENTT_NOEXCEPT +: calls{std::move(other.calls), allocator} {} + +/** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This signal handler. + */ +sigh &operator=(const sigh &other) { +calls = other.calls; +return *this; +} + +/** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This signal handler. + */ +sigh &operator=(sigh &&other) ENTT_NOEXCEPT { +calls = std::move(other.calls); +return *this; +} + +/** + * @brief Exchanges the contents with those of a given signal handler. + * @param other Signal handler to exchange the content with. + */ +void swap(sigh &other) { +using std::swap; +swap(calls, other.calls); +} + +/** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ +[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { +return calls.get_allocator(); +} + +/** + * @brief Instance type when it comes to connecting member functions. + * @tparam Class Type of class to which the member function belongs. + */ +template<typename Class> +using instance_type = Class *; + +/** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ +[[nodiscard]] size_type size() const ENTT_NOEXCEPT { +return calls.size(); +} + +/** + * @brief Returns false if at least a listener is connected to the signal. + * @return True if the signal has no listeners connected, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +return calls.empty(); +} + +/** + * @brief Triggers a signal. + * + * All the listeners are notified. Order isn't guaranteed. + * + * @param args Arguments to use to invoke listeners. + */ +void publish(Args... args) const { +for(auto &&call: std::as_const(calls)) { +call(args...); +} +} + +/** + * @brief Collects return values from the listeners. + * + * The collector must expose a call operator with the following properties: + * + * * The return type is either `void` or such that it's convertible to + * `bool`. In the second case, a true value will stop the iteration. + * * The list of parameters is empty if `Ret` is `void`, otherwise it + * contains a single element such that `Ret` is convertible to it. + * + * @tparam Func Type of collector to use, if any. + * @param func A valid function object. + * @param args Arguments to use to invoke listeners. + */ +template<typename Func> +void collect(Func func, Args... args) const { +for(auto &&call: calls) { +if constexpr(std::is_void_v<Ret>) { +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...))) { break; } +} else { +func(call(args...)); +} +} +} +} + +private: +container_type calls; +}; + +/** + * @brief Connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it. + */ +class connection { +/*! @brief A sink is allowed to create connection objects. */ +template<typename> +friend class sink; + +connection(delegate<void(void *)> fn, void *ref) +: disconnect{fn}, signal{ref} {} + +public: +/*! @brief Default constructor. */ +connection() = default; + +/** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(disconnect); +} + +/*! @brief Breaks the connection. */ +void release() { +if(disconnect) { +disconnect(signal); +disconnect.reset(); +} +} + +private: +delegate<void(void *)> disconnect; +void *signal{}; +}; + +/** + * @brief Scoped connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it.<br/> + * A scoped connection automatically breaks the link between the two objects + * when it goes out of scope. + */ +struct scoped_connection { +/*! @brief Default constructor. */ +scoped_connection() = default; + +/** + * @brief Constructs a scoped connection from a basic connection. + * @param other A valid connection object. + */ +scoped_connection(const connection &other) +: conn{other} {} + +/*! @brief Default copy constructor, deleted on purpose. */ +scoped_connection(const scoped_connection &) = delete; + +/** + * @brief Move constructor. + * @param other The scoped connection to move from. + */ +scoped_connection(scoped_connection &&other) ENTT_NOEXCEPT +: conn{std::exchange(other.conn, {})} {} + +/*! @brief Automatically breaks the link on destruction. */ +~scoped_connection() { +conn.release(); +} + +/** + * @brief Default copy assignment operator, deleted on purpose. + * @return This scoped connection. + */ +scoped_connection &operator=(const scoped_connection &) = delete; + +/** + * @brief Move assignment operator. + * @param other The scoped connection to move from. + * @return This scoped connection. + */ +scoped_connection &operator=(scoped_connection &&other) ENTT_NOEXCEPT { +conn = std::exchange(other.conn, {}); +return *this; +} + +/** + * @brief Acquires a connection. + * @param other The connection object to acquire. + * @return This scoped connection. + */ +scoped_connection &operator=(connection other) { +conn = std::move(other); +return *this; +} + +/** + * @brief Checks whether a scoped connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ +[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { +return static_cast<bool>(conn); +} + +/*! @brief Breaks the connection. */ +void release() { +conn.release(); +} + +private: +connection conn; +}; + +/** + * @brief Sink class. + * + * A sink is used to connect listeners to signals and to disconnect them.<br/> + * The function type for a listener is the one of the signal to which it + * belongs. + * + * The clear separation between a signal and a sink permits to store the former + * as private data member without exposing the publish functionality to the + * users of the class. + * + * @warning + * Lifetime of a sink must not overcome that of the signal to which it refers. + * In any other case, attempting to use a sink results in undefined behavior. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Ret, typename... Args, typename Allocator> +class sink<sigh<Ret(Args...), Allocator>> { +using signal_type = sigh<Ret(Args...), Allocator>; +using difference_type = typename signal_type::container_type::difference_type; + +template<auto Candidate, typename Type> +static void release(Type value_or_instance, void *signal) { +sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>(value_or_instance); +} + +template<auto Candidate> +static void release(void *signal) { +sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>(); +} + +public: +/** + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. + */ +sink(sigh<Ret(Args...), Allocator> &ref) ENTT_NOEXCEPT +: offset{}, +signal{&ref} {} + +/** + * @brief Returns false if at least a listener is connected to the sink. + * @return True if the sink has no listeners connected, false otherwise. + */ +[[nodiscard]] bool empty() const ENTT_NOEXCEPT { +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>(); + +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; +} + +/** + * @brief Returns a sink that connects before a free function with payload + * or a bound member. + * @tparam Candidate Member or free function to look for. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ +template<auto Candidate, typename Type> +[[nodiscard]] sink before(Type &&value_or_instance) { +delegate<Ret(Args...)> call{}; +call.template connect<Candidate>(value_or_instance); + +const auto &calls = signal->calls; +const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + +sink other{*this}; +other.offset = calls.cend() - it; +return other; +} + +/** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ +template<typename Type> +[[nodiscard]] sink before(Type &value_or_instance) { +return before(&value_or_instance); +} + +/** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + * @return A properly initialized sink object. + */ +template<typename Type> +[[nodiscard]] sink before(Type *value_or_instance) { +sink other{*this}; + +if(value_or_instance) { +const auto &calls = signal->calls; +const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) { +return delegate.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 or an unbound member to a signal. + * + * The signal handler performs checks to avoid multiple connections for the + * same function. + * + * @tparam Candidate Function or member to connect to the signal. + * @return A properly initialized connection object. + */ +template<auto Candidate> +connection connect() { +disconnect<Candidate>(); + +delegate<Ret(Args...)> call{}; +call.template connect<Candidate>(); +signal->calls.insert(signal->calls.end() - offset, std::move(call)); + +delegate<void(void *)> conn{}; +conn.template connect<&release<Candidate>>(); +return {std::move(conn), signal}; +} + +/** + * @brief Connects a free function with payload or a bound member to a + * signal. + * + * The signal isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the 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. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized connection object. + */ +template<auto Candidate, typename Type> +connection connect(Type &&value_or_instance) { +disconnect<Candidate>(value_or_instance); + +delegate<Ret(Args...)> call{}; +call.template connect<Candidate>(value_or_instance); +signal->calls.insert(signal->calls.end() - offset, std::move(call)); + +delegate<void(void *)> conn{}; +conn.template connect<&release<Candidate, Type>>(value_or_instance); +return {std::move(conn), signal}; +} + +/** + * @brief Disconnects a free function or an unbound member from a signal. + * @tparam Candidate Function or member to disconnect from the signal. + */ +template<auto Candidate> +void disconnect() { +auto &calls = signal->calls; +delegate<Ret(Args...)> call{}; +call.template connect<Candidate>(); +calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); +} + +/** + * @brief Disconnects a free function with payload or a bound member from a + * signal. + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ +template<auto Candidate, typename Type> +void disconnect(Type &&value_or_instance) { +auto &calls = signal->calls; +delegate<Ret(Args...)> call{}; +call.template connect<Candidate>(value_or_instance); +calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); +} + +/** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ +template<typename Type> +void disconnect(Type &value_or_instance) { +disconnect(&value_or_instance); +} + +/** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ +template<typename Type> +void disconnect(Type *value_or_instance) { +if(value_or_instance) { +auto &calls = signal->calls; +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()); +} +} + +/*! @brief Disconnects all the listeners from a signal. */ +void disconnect() { +signal->calls.clear(); +} + +private: +difference_type offset; +signal_type *signal; +}; + +/** + * @brief Deduction guide. + * + * It allows to deduce the signal handler type of a sink directly from the + * signal it refers to. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template<typename Ret, typename... Args, typename Allocator> +sink(sigh<Ret(Args...), Allocator> &) -> sink<sigh<Ret(Args...), Allocator>>; + +} // namespace entt + +#endif \ No newline at end of file diff --git a/tests/testfggl/util/guid.cpp b/tests/testfggl/util/guid.cpp index e4dedf7b8280a2c2eceb322f38030adb5120ef2e..d70fa0f5dd2ca31241d4368ef68c95a5822cb01e 100644 --- a/tests/testfggl/util/guid.cpp +++ b/tests/testfggl/util/guid.cpp @@ -71,12 +71,16 @@ namespace { auto value = fggl::util::hash_fnv1a_32("fggl::test::scoped"); auto value2 = fggl::util::hash_fnv1a_32("fggl::test::scoped"); EXPECT_EQ( value, value2 ); + EXPECT_EQ( false, value < value2); + EXPECT_EQ( false, value2 < value); } TEST(UtilHash64, RepeatsAreEqual) { auto value = fggl::util::hash_fnv1a_64("fggl::test::scoped"); auto value2 = fggl::util::hash_fnv1a_64("fggl::test::scoped"); EXPECT_EQ( value, value2 ); + EXPECT_EQ( false, value < value2); + EXPECT_EQ( false, value2 < value); } // GUID tests @@ -84,6 +88,24 @@ namespace { auto guid = fggl::util::make_guid("test"); auto guidSuffix = "test"_fid; EXPECT_EQ(guid, guidSuffix); + EXPECT_EQ( false, guid < guidSuffix); + EXPECT_EQ( false, guidSuffix < guid); + } + + TEST(GuidTest, OrderingLE) { + auto guid1 = fggl::util::GUID::make(0); + auto guid2 = fggl::util::GUID::make(1); + EXPECT_NE(guid1, guid2); + EXPECT_EQ( true, guid1 < guid2); + EXPECT_EQ( false, guid2 < guid1); + } + + TEST(GuidTest, OrderingGE) { + auto guid1 = fggl::util::GUID::make(15); + auto guid2 = fggl::util::GUID::make(36); + EXPECT_NE(guid1, guid2); + EXPECT_EQ( true, guid1 < guid2); + EXPECT_EQ( false, guid2 < guid1); } TEST(GuidTest, GuidToString) {