From 0f010e0e74334a423f865e683d1e96f66bcf6247 Mon Sep 17 00:00:00 2001 From: Antoine Pilote Date: Tue, 29 Jun 2021 21:47:37 -0400 Subject: [PATCH] Major editor upgrade, bug fixes, and more. --- Editor/resources/Scenes/test.scene | 39 +- Editor/src/EditorInterface.cpp | 76 +- Editor/src/FileSystemUI.cpp | 10 + Nuake/dependencies/assimp.lua | 123 - Nuake/dependencies/assimp_p5.lua | 11 +- Nuake/src/Core/OS.h | 9 + Nuake/src/Rendering/Textures/Texture.cpp | 3 +- Nuake/src/Scene/Components/LightComponent.cpp | 2 +- Nuake/src/Scene/Components/MeshComponent.cpp | 2 +- Nuake/src/Scene/Components/ModelComponent.cpp | 34 +- Nuake/src/Scene/Components/ModelComponent.h | 5 +- Nuake/src/Scene/Components/NameComponent.h | 11 +- Nuake/src/Scene/Components/ParentComponent.h | 21 +- Nuake/src/Scene/Entities/Entity.h | 16 +- Nuake/src/Scene/Scene.cpp | 57 +- Nuake/src/Scene/Scene.h | 4 + Nuake/src/Scene/SceneArchive.h | 21 + Nuake/src/Scene/Systems/QuakeMapBuilder.cpp | 9 +- Nuake/src/Scene/Systems/ScriptingSystem.cpp | 3 +- Nuake/src/Vendors/entt/entt.hpp | 36554 +++++++++++++--- 20 files changed, 30833 insertions(+), 6177 deletions(-) delete mode 100644 Nuake/dependencies/assimp.lua create mode 100644 Nuake/src/Core/OS.h create mode 100644 Nuake/src/Scene/SceneArchive.h diff --git a/Editor/resources/Scenes/test.scene b/Editor/resources/Scenes/test.scene index 3edfafd6..2e6913a6 100644 --- a/Editor/resources/Scenes/test.scene +++ b/Editor/resources/Scenes/test.scene @@ -1,5 +1,32 @@ { "Entities": [ + { + "NameComponent": { + "ID": -1598095916, + "Name": "Model" + }, + "ParentComponent": { + "HasParent": false + }, + "TransformComponent": { + "Rotation": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "Scale": { + "x": 1.0, + "y": 1.0, + "z": 1.0 + }, + "Translation": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "Type": "TransformComponent" + } + }, { "CharacterControllerComponent": { "Height": 1.2000000476837158, @@ -7,6 +34,7 @@ "Radius": 0.20000000298023224 }, "NameComponent": { + "ID": 237171289, "Name": "Player" }, "ParentComponent": { @@ -54,6 +82,7 @@ "Type": 0 }, "NameComponent": { + "ID": 3, "Name": "Light" }, "ParentComponent": { @@ -84,6 +113,7 @@ }, { "NameComponent": { + "ID": 237443808, "Name": "Trenchbroom map" }, "ParentComponent": { @@ -123,6 +153,7 @@ } }, "NameComponent": { + "ID": 237481018, "Name": "Camera" }, "ParentComponent": { @@ -155,10 +186,10 @@ "Name": "", "m_Environement": { "AmbientColor": { - "w": 0.0, - "x": 0.0, - "y": 0.0, - "z": 0.0 + "w": -431602080.0, + "x": -431602080.0, + "y": -431602080.0, + "z": -431602080.0 }, "ProceduralSkybox": { "AtmosphereRadius": 6380000.0, diff --git a/Editor/src/EditorInterface.cpp b/Editor/src/EditorInterface.cpp index 8a2f37d3..a1aa1845 100644 --- a/Editor/src/EditorInterface.cpp +++ b/Editor/src/EditorInterface.cpp @@ -230,6 +230,7 @@ void EditorInterface::DrawEntityTree(Entity e) bool open = ImGui::TreeNodeEx(name.c_str(), base_flags); + if (ImGui::BeginDragDropSource()) { ImGui::SetDragDropPayload("ENTITY", (void*)&e, sizeof(Entity)); @@ -267,7 +268,27 @@ void EditorInterface::DrawEntityTree(Entity e) m_SelectedEntity = e; m_IsEntitySelected = true; } + if (ImGui::BeginPopupContextItem()) + { + if (ImGui::Selectable("Remove")) { + Engine::GetCurrentScene()->DestroyEntity(m_SelectedEntity); + m_SelectedEntity = Engine::GetCurrentScene()->GetAllEntities()[0]; + open = false; + } + if (ImGui::Selectable("Move to root")) + { + auto& p = m_SelectedEntity.GetComponent(); + if (p.HasParent) { + auto& pp = p.Parent.GetComponent(); + pp.RemoveChildren(m_SelectedEntity); + p.HasParent = false; + } + } + //m_SelectedEntity.Add + ImGui::Selectable("Save as prefab"); + ImGui::EndPopup(); + } if (open) { for (auto child : parent.Children) @@ -338,11 +359,11 @@ void EditorInterface::DrawSceneTree() ImGui::Separator(); // Draw a tree of entities. - for (Entity e : scene->GetAllEntities()) + std::vector entities = scene->GetAllEntities(); + for (Entity e : entities) { ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth; std::string name = e.GetComponent().Name; - // If selected add selected flag. if (m_SelectedEntity == e) base_flags |= ImGuiTreeNodeFlags_Selected; @@ -416,8 +437,8 @@ void EditorInterface::DrawEntityPropreties() if (ImGui::MenuItem("Light Component") && !m_SelectedEntity.HasComponent()) m_SelectedEntity.AddComponent(); ImGui::Separator(); - if (ImGui::MenuItem("Mesh Component") && !m_SelectedEntity.HasComponent()) - m_SelectedEntity.AddComponent(); + if (ImGui::MenuItem("Model Component") && !m_SelectedEntity.HasComponent()) + m_SelectedEntity.AddComponent(); if (ImGui::MenuItem("Quake map Component") && !m_SelectedEntity.HasComponent()) m_SelectedEntity.AddComponent(); ImGui::Separator(); @@ -460,6 +481,53 @@ void EditorInterface::DrawEntityPropreties() ImGui::Separator(); } + if (m_SelectedEntity.HasComponent()) { + std::string icon = ICON_FA_MALE; + if (ImGui::CollapsingHeader((icon + " " + "Model").c_str(), ImGuiTreeNodeFlags_DefaultOpen)) + { + ImGui::TextColored(ImGui::GetStyleColorVec4(1), "Model properties"); + auto& component = m_SelectedEntity.GetComponent(); + // Path + std::string path = component.ModelPath; + char pathBuffer[256]; + + memset(pathBuffer, 0, sizeof(pathBuffer)); + std::strncpy(pathBuffer, path.c_str(), sizeof(pathBuffer)); + + std::string oldPath = component.ModelPath; + ImGui::Text("Model: "); + ImGui::SameLine(); + if (ImGui::InputText("##ModelPath", pathBuffer, sizeof(pathBuffer))) + path = FileSystem::AbsoluteToRelative(std::string(pathBuffer)); + if (ImGui::BeginDragDropTarget()) + { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("_Model")) + { + char* file = (char*)payload->Data; + std::string fullPath = std::string(file, 256); + path = FileSystem::AbsoluteToRelative(fullPath); + } + ImGui::EndDragDropTarget(); + } + + if (component.ModelPath != path) + { + component.ModelPath = path; + component.LoadModel(); + } + + + if (ImGui::Button("Reimport")) + { + component.LoadModel(); + } + + + ImGui::Separator(); + } + + } + if (m_SelectedEntity.HasComponent()) { std::string icon = ICON_FA_LIGHTBULB; if (ImGui::CollapsingHeader((icon + " " + "Light").c_str(), ImGuiTreeNodeFlags_DefaultOpen)) diff --git a/Editor/src/FileSystemUI.cpp b/Editor/src/FileSystemUI.cpp index f3ff52e1..f213f78a 100644 --- a/Editor/src/FileSystemUI.cpp +++ b/Editor/src/FileSystemUI.cpp @@ -110,6 +110,8 @@ void FileSystemUI::DrawFile(Ref file) icon = ICON_FA_FILE_AUDIO; if (file->Type == ".wren") icon = ICON_FA_FILE_CODE; + if (file->Type == ".md3" || file->Type == ".obj") + icon = ICON_FA_FILE_IMAGE; std::string fullName = icon + std::string("##") + file->fullPath; if (ImGui::Button(fullName.c_str(), ImVec2(100, 100))) @@ -126,6 +128,9 @@ void FileSystemUI::DrawFile(Ref file) dragType = "_Script"; else if (file->Type == ".map") dragType = "_Map"; + else if (file->Type == ".obj" || file->Type == ".mdl" || file->Type == ".gltf" || file->Type == ".md3" || file->Type == "fbx") + dragType = "_Model"; + ImGui::SetDragDropPayload(dragType.c_str(), (void*)(pathBuffer), sizeof(pathBuffer)); ImGui::Text(file->name.c_str()); ImGui::EndDragDropSource(); @@ -210,6 +215,11 @@ void FileSystemUI::DrawDirectoryExplorer() avail.y = 30; if (ImGui::BeginChild("ariane", avail)) { + if (ImGui::Button("Refresh")) + FileSystem::Scan(); + + ImGui::SameLine(); + for (int i = paths.size() - 1; i > 0; i--) { if (i != paths.size()) ImGui::SameLine(); diff --git a/Nuake/dependencies/assimp.lua b/Nuake/dependencies/assimp.lua deleted file mode 100644 index 10d74cae..00000000 --- a/Nuake/dependencies/assimp.lua +++ /dev/null @@ -1,123 +0,0 @@ -project "assimp" - kind "StaticLib" - language "C++" - - targetdir ("bin/" .. outputdir .. "/%{prj.name}") - objdir ("bin-int/" .. outputdir .. "/%{prj.name}") - - files - { - -- Dependencies - "contrib/unzip/**", - "contrib/irrXML/**", - "contrib/zlib/*", - -- Common - "code/Common/**", - "code/PostProcessing/**", - "code/Material/**", - "code/CApi/**", - -- Importers - "code/Collada/**", - "code/Obj/**", - -- "code/Blender/**", "contrib/poly2tri/poly2tri/**", - "code/FBX/**", - -- "code/glTF2/**", - -- "code/glTF/**", - "code/Assbin/**" -- For caching - } - - includedirs - { - "contrib/irrXML/", - "contrib/unzip/", - "contrib/rapidjson/include/", - "code", - "include" - } - - filter "system:windows" - systemversion "latest" - staticruntime "On" - defines { - "ASSIMP_BUILD_NO_3D_IMPORTER", - "ASSIMP_BUILD_NO_3DS_IMPORTER", - "ASSIMP_BUILD_NO_3MF_IMPORTER", - "ASSIMP_BUILD_NO_AC_IMPORTER", - "ASSIMP_BUILD_NO_AMF_IMPORTER", - "ASSIMP_BUILD_NO_ASE_IMPORTER", - -- "ASSIMP_BUILD_NO_ASSBIN_IMPORTER" - "ASSIMP_BUILD_NO_B3D_IMPORTER", - "ASSIMP_BUILD_NO_BLEND_IMPORTER", - "ASSIMP_BUILD_NO_BVH_IMPORTER", - "ASSIMP_BUILD_NO_C4D_IMPORTER", - "ASSIMP_BUILD_NO_COB_IMPORTER", - -- "ASSIMP_BUILD_NO_COLLADA_IMPORTER", - "ASSIMP_BUILD_NO_CSM_IMPORTER", - "ASSIMP_BUILD_NO_DXF_IMPORTER", - -- "ASSIMP_BUILD_NO_FBX_IMPORTER", - "ASSIMP_BUILD_NO_GLTF_IMPORTER", - "ASSIMP_BUILD_NO_HMP_IMPORTER", - "ASSIMP_BUILD_NO_IFC_IMPORTER", - "ASSIMP_BUILD_NO_IRR_IMPORTER", - "ASSIMP_BUILD_NO_IRRMESH_IMPORTER", - "ASSIMP_BUILD_NO_LWO_IMPORTER", - "ASSIMP_BUILD_NO_LWS_IMPORTER", - "ASSIMP_BUILD_NO_M3D_IMPORTER", - "ASSIMP_BUILD_NO_MD2_IMPORTER", - "ASSIMP_BUILD_NO_MD3_IMPORTER", - "ASSIMP_BUILD_NO_MD5_IMPORTER", - "ASSIMP_BUILD_NO_MDC_IMPORTER", - "ASSIMP_BUILD_NO_MDL_IMPORTER", - "ASSIMP_BUILD_NO_MMD_IMPORTER", - "ASSIMP_BUILD_NO_MS3D_IMPORTER", - "ASSIMP_BUILD_NO_NDO_IMPORTER", - "ASSIMP_BUILD_NO_NFF_IMPORTER", - -- "ASSIMP_BUILD_NO_OBJ_IMPORTER", - "ASSIMP_BUILD_NO_OFF_IMPORTER", - "ASSIMP_BUILD_NO_OGRE_IMPORTER", - "ASSIMP_BUILD_NO_OPENGEX_IMPORTER", - "ASSIMP_BUILD_NO_PLY_IMPORTER", - "ASSIMP_BUILD_NO_Q3BSP_IMPORTER", - "ASSIMP_BUILD_NO_Q3D_IMPORTER", - "ASSIMP_BUILD_NO_RAW_IMPORTER", - "ASSIMP_BUILD_NO_SIB_IMPORTER", - "ASSIMP_BUILD_NO_SMD_IMPORTER", - "ASSIMP_BUILD_NO_STEP_IMPORTER", - "ASSIMP_BUILD_NO_STL_IMPORTER", - "ASSIMP_BUILD_NO_TERRAGEN_IMPORTER", - "ASSIMP_BUILD_NO_X_IMPORTER", - "ASSIMP_BUILD_NO_X3D_IMPORTER", - "ASSIMP_BUILD_NO_XGL_IMPORTER" - } - -- Exporters - defines { - "ASSIMP_BUILD_NO_COLLADA_EXPORTER", - "ASSIMP_BUILD_NO_X_EXPORTER", - "ASSIMP_BUILD_NO_STEP_EXPORTER", - "ASSIMP_BUILD_NO_OBJ_EXPORTER", - "ASSIMP_BUILD_NO_STL_EXPORTER", - "ASSIMP_BUILD_NO_PLY_EXPORTER", - "ASSIMP_BUILD_NO_3DS_EXPORTER", - "ASSIMP_BUILD_NO_GLTF_EXPORTER", - -- "ASSIMP_BUILD_NO_ASSBIN_EXPORTER", - "ASSIMP_BUILD_NO_ASSXML_EXPORTER", - "ASSIMP_BUILD_NO_X3D_EXPORTER", - "ASSIMP_BUILD_NO_FBX_EXPORTER", - "ASSIMP_BUILD_NO_M3D_EXPORTER", - "ASSIMP_BUILD_NO_3MF_EXPORTER", - "ASSIMP_BUILD_NO_ASSJSON_EXPORTER" - } - - filter "system:linux" - pic "On" - systemversion "latest" - cppdialect "C++17" - staticruntime "On" - - filter "configurations:Debug" - runtime "Debug" - symbols "on" - - filter "configurations:Release" - runtime "Release" - optimize "on" \ No newline at end of file diff --git a/Nuake/dependencies/assimp_p5.lua b/Nuake/dependencies/assimp_p5.lua index 27dc3ea9..0dcf65ca 100644 --- a/Nuake/dependencies/assimp_p5.lua +++ b/Nuake/dependencies/assimp_p5.lua @@ -38,7 +38,10 @@ project 'assimp' 'assimp/code/AssetLib/FBX/**', '../src/Vendors/pugixml/**', -- 'assimp/code/glTF2/**', - -- 'assimp/code/glTF/**', + 'assimp/code/AssetLib/glTF/**', + 'assimp/code/AssetLib/MDL/**', + 'assimp/code/AssetLib/MD2/**', + 'assimp/code/AssetLib/MD3/**', 'assimp/code/Assbin/**' -- For caching } -- Importers @@ -68,11 +71,11 @@ project 'assimp' 'ASSIMP_BUILD_NO_LWO_IMPORTER', 'ASSIMP_BUILD_NO_LWS_IMPORTER', 'ASSIMP_BUILD_NO_M3D_IMPORTER', - 'ASSIMP_BUILD_NO_MD2_IMPORTER', - 'ASSIMP_BUILD_NO_MD3_IMPORTER', + --'ASSIMP_BUILD_NO_MD2_IMPORTER', + --'ASSIMP_BUILD_NO_MD3_IMPORTER', 'ASSIMP_BUILD_NO_MD5_IMPORTER', 'ASSIMP_BUILD_NO_MDC_IMPORTER', - 'ASSIMP_BUILD_NO_MDL_IMPORTER', + --'ASSIMP_BUILD_NO_MDL_IMPORTER', 'ASSIMP_BUILD_NO_MMD_IMPORTER', 'ASSIMP_BUILD_NO_MS3D_IMPORTER', 'ASSIMP_BUILD_NO_NDO_IMPORTER', diff --git a/Nuake/src/Core/OS.h b/Nuake/src/Core/OS.h new file mode 100644 index 00000000..4f7aa411 --- /dev/null +++ b/Nuake/src/Core/OS.h @@ -0,0 +1,9 @@ +#pragma once + + +class OS { +public: + static int GetTime() { + return std::chrono::system_clock::now().time_since_epoch().count(); + } +}; \ No newline at end of file diff --git a/Nuake/src/Rendering/Textures/Texture.cpp b/Nuake/src/Rendering/Textures/Texture.cpp index 1410c828..ede23986 100644 --- a/Nuake/src/Rendering/Textures/Texture.cpp +++ b/Nuake/src/Rendering/Textures/Texture.cpp @@ -1,6 +1,7 @@ #include "Texture.h" #include #include +#include Texture::Texture(const std::string& path) { m_RendererId = 0; @@ -12,7 +13,7 @@ Texture::Texture(const std::string& path) { stbi_set_flip_vertically_on_load(1); - m_LocalBuffer = stbi_load(path.c_str(), &m_Width, &m_Height, &m_BPP, 4); + m_LocalBuffer = stbi_load((path).c_str(), &m_Width, &m_Height, &m_BPP, 4); glGenTextures(1, &m_RendererId); glBindTexture(GL_TEXTURE_2D, m_RendererId); diff --git a/Nuake/src/Scene/Components/LightComponent.cpp b/Nuake/src/Scene/Components/LightComponent.cpp index c917e59b..b99214ab 100644 --- a/Nuake/src/Scene/Components/LightComponent.cpp +++ b/Nuake/src/Scene/Components/LightComponent.cpp @@ -104,7 +104,7 @@ void LightComponent::DrawDeferred(TransformComponent transformComponent, Camera* void LightComponent::DrawEditor() { ImGui::TextColored(ImGui::GetStyleColorVec4(1), "Light properties"); - ImGui::ColorEdit4("Light Color", &Color.r); + ImGui::ColorEdit3("Light Color", &Color.r); ImGui::SliderFloat("Strength", &Strength, 0.0f, 50.0f); bool before = CastShadows; ImGui::Checkbox("Cast shadows", &CastShadows); diff --git a/Nuake/src/Scene/Components/MeshComponent.cpp b/Nuake/src/Scene/Components/MeshComponent.cpp index 9f3c7104..9b099b72 100644 --- a/Nuake/src/Scene/Components/MeshComponent.cpp +++ b/Nuake/src/Scene/Components/MeshComponent.cpp @@ -218,7 +218,7 @@ MeshComponent::MeshComponent() { glVertexAttribPointer(5, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(sizeof(GL_FLOAT) * 14)); glEnableVertexAttribArray(5); - m_Material = MaterialManager::Get()->LoadMaterial("Planks"); + //m_Material = MaterialManager::Get()->LoadMaterial("Planks"); } diff --git a/Nuake/src/Scene/Components/ModelComponent.cpp b/Nuake/src/Scene/Components/ModelComponent.cpp index a7d49db1..45c88dd1 100644 --- a/Nuake/src/Scene/Components/ModelComponent.cpp +++ b/Nuake/src/Scene/Components/ModelComponent.cpp @@ -10,18 +10,18 @@ void ModelComponent::Draw() } } -void ModelComponent::LoadModel(std::string path) +void ModelComponent::LoadModel() { + this->meshes.clear(); Assimp::Importer import; import.SetPropertyFloat("PP_GSN_MAX_SMOOTHING_ANGLE", 90); - const aiScene* scene = import.ReadFile(path, aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_CalcTangentSpace); + const aiScene* scene = import.ReadFile(FileSystem::Root + ModelPath, aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_CalcTangentSpace); if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { - printf("ERROR::ASSIMP::{0}", import.GetErrorString()); + Logger::Log("ASSIMP! Failed to load model" + std::string(import.GetErrorString()), CRITICAL); return; } - directory = path.substr(0, path.find_last_of('/')) + "/"; ProcessNode(scene->mRootNode, scene); } @@ -97,24 +97,24 @@ Mesh ModelComponent::ProcessMesh(aiMesh* mesh, const aiScene* scene) { aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex]; aiString str; - + std::string directory = FileSystem::Root + this->ModelPath + "/../"; material->GetTexture(aiTextureType_DIFFUSE, 0, &str); Ref newMaterial = CreateRef(TextureManager::Get()->GetTexture(directory + str.C_Str())); - material->GetTexture(aiTextureType_NORMALS, 0, &str); - newMaterial->SetNormal(TextureManager::Get()->GetTexture(directory + str.C_Str())); + //material->GetTexture(aiTextureType_NORMALS, 0, &str); + //newMaterial->SetNormal(TextureManager::Get()->GetTexture(directory + str.C_Str())); + // + //material->GetTexture(aiTextureType_METALNESS, 0, &str); + //newMaterial->SetMetalness(TextureManager::Get()->GetTexture(directory + str.C_Str())); + // + //material->GetTexture(aiTextureType_DIFFUSE_ROUGHNESS, 0, &str); + //newMaterial->SetRoughness(TextureManager::Get()->GetTexture(directory + str.C_Str())); - material->GetTexture(aiTextureType_METALNESS, 0, &str); - newMaterial->SetMetalness(TextureManager::Get()->GetTexture(directory + str.C_Str())); + //material->GetTexture(aiTextureType_DISPLACEMENT, 0, &str); + //newMaterial->SetDisplacement(TextureManager::Get()->GetTexture(directory + str.C_Str())); - material->GetTexture(aiTextureType_DIFFUSE_ROUGHNESS, 0, &str); - newMaterial->SetRoughness(TextureManager::Get()->GetTexture(directory + str.C_Str())); - - material->GetTexture(aiTextureType_DISPLACEMENT, 0, &str); - newMaterial->SetDisplacement(TextureManager::Get()->GetTexture(directory + str.C_Str())); - - material->GetTexture(aiTextureType_AMBIENT_OCCLUSION, 0, &str); - newMaterial->SetAO(TextureManager::Get()->GetTexture(directory + str.C_Str())); + //material->GetTexture(aiTextureType_AMBIENT_OCCLUSION, 0, &str); + //newMaterial->SetAO(TextureManager::Get()->GetTexture(directory + str.C_Str())); return Mesh(vertices, indices, newMaterial); } diff --git a/Nuake/src/Scene/Components/ModelComponent.h b/Nuake/src/Scene/Components/ModelComponent.h index fbb46e1c..df6d9452 100644 --- a/Nuake/src/Scene/Components/ModelComponent.h +++ b/Nuake/src/Scene/Components/ModelComponent.h @@ -6,14 +6,17 @@ #include "assimp/Importer.hpp" #include #include +#include class ModelComponent { public: + std::string ModelPath; + ModelComponent() { //loadModel(path); } - void LoadModel(std::string path); + void LoadModel(); void Draw(); private: // model data diff --git a/Nuake/src/Scene/Components/NameComponent.h b/Nuake/src/Scene/Components/NameComponent.h index 6a527c3c..9431fce9 100644 --- a/Nuake/src/Scene/Components/NameComponent.h +++ b/Nuake/src/Scene/Components/NameComponent.h @@ -1,14 +1,16 @@ #pragma once #include "../Resource/Serializable.h" - +#include "../Core/OS.h" class NameComponent { public: std::string Name = "Entity"; - int Id; + int ID; + json Serialize() { BEGIN_SERIALIZE(); SERIALIZE_VAL(Name); + SERIALIZE_VAL(ID); END_SERIALIZE(); } @@ -16,6 +18,11 @@ public: { BEGIN_DESERIALIZE(); Name = j["Name"]; + if (j.contains("ID")) + ID = j["ID"]; + else + ID = OS::GetTime(); + return true; } }; \ No newline at end of file diff --git a/Nuake/src/Scene/Components/ParentComponent.h b/Nuake/src/Scene/Components/ParentComponent.h index 1b1ddb77..e2c1d052 100644 --- a/Nuake/src/Scene/Components/ParentComponent.h +++ b/Nuake/src/Scene/Components/ParentComponent.h @@ -4,16 +4,31 @@ struct ParentComponent { + int ParentID; Entity Parent; bool HasParent = false; std::vector Children = std::vector(); + bool RemoveChildren(Entity ent) + { + for (int i = 0; i < Children.size(); i++) + { + if (Children[i].GetHandle() == ent.GetHandle()) + { + Children.erase(Children.begin() + i); + return true; + } + } + + return false; + } + json Serialize() { BEGIN_SERIALIZE(); SERIALIZE_VAL(HasParent); if(HasParent) - SERIALIZE_VAL_LBL("Parent", Parent.GetHandle()); + SERIALIZE_VAL_LBL("ParentID", Parent.GetID()); //int i = 0; //for (auto& c : Children) { @@ -27,10 +42,12 @@ struct ParentComponent bool Deserialize(std::string str) { BEGIN_DESERIALIZE(); + this->HasParent = j["HasParent"]; + if(HasParent) + this->ParentID = j["ParentID"]; //this->Parent = Entity{ j["Parent"], Engine::GetCurrentScene().get() }; return true; - } }; \ No newline at end of file diff --git a/Nuake/src/Scene/Entities/Entity.h b/Nuake/src/Scene/Entities/Entity.h index 75ed12c4..0ffc6ad2 100644 --- a/Nuake/src/Scene/Entities/Entity.h +++ b/Nuake/src/Scene/Entities/Entity.h @@ -4,7 +4,7 @@ #include "../Scene.h" #include "../Components/BaseComponent.h" #include "../Resource/Serializable.h" - +#include "../Components/NameComponent.h" class Entity : public ISerializable { public: @@ -15,9 +15,15 @@ public: void AddChild(Entity ent); int GetHandle() { return (int)m_EntityHandle; } + int GetID() { return GetComponent().ID; } + template bool HasComponent() { - return m_Scene->m_Registry.has(m_EntityHandle); + return m_Scene->m_Registry.all_of(m_EntityHandle); + } + + bool IsValid() { + return m_Scene->m_Registry.valid((entt::entity)GetHandle()); } template @@ -37,9 +43,9 @@ public: return component; } - void Destroy() { - if(m_Scene->m_Registry.valid(m_EntityHandle)) - m_Scene->m_Registry.destroy(m_EntityHandle); + void Destroy() { + Logger::Log("Deleted eneity :" + std::to_string((int)m_EntityHandle)); + m_Scene->m_Registry.destroy(m_EntityHandle); } bool operator==(const Entity& other) const diff --git a/Nuake/src/Scene/Scene.cpp b/Nuake/src/Scene/Scene.cpp index aa1e0667..af973d56 100644 --- a/Nuake/src/Scene/Scene.cpp +++ b/Nuake/src/Scene/Scene.cpp @@ -25,7 +25,8 @@ #include #include - +#include +#include Ref Scene::New() { @@ -65,6 +66,16 @@ Entity Scene::GetEntity(int handle) return Entity((entt::entity)handle, this); } +Entity Scene::GetEntityByID(int id) +{ + auto idView = m_Registry.view(); + for (auto e : idView) { + NameComponent& nameC = idView.get(e); + if (nameC.ID == id) + return Entity{ e, this }; + } +} + void Scene::OnInit() { for (auto& system : m_Systems) @@ -112,6 +123,7 @@ void Scene::UpdatePositions() { while (currentParent.GetComponent().HasParent) { currentParent = currentParent.GetComponent().Parent; + globalPos += currentParent.GetComponent().Translation; } @@ -351,7 +363,11 @@ std::vector Scene::GetAllEntities() { std::vector allEntities; auto view = m_Registry.view(); for (auto e : view) { - allEntities.push_back(Entity(e, this)); + Entity newEntity(e, this); + + // Check if valid for deleted entities. + if(newEntity.IsValid()) + allEntities.push_back(newEntity); } return allEntities; } @@ -394,6 +410,7 @@ Entity Scene::CreateEntity(const std::string& name) { NameComponent& nameComponent = entity.AddComponent(); nameComponent.Name = name; + nameComponent.ID = (int)OS::GetTime(); ParentComponent& parentComponent = entity.AddComponent(); @@ -403,15 +420,20 @@ Entity Scene::CreateEntity(const std::string& name) { void Scene::DestroyEntity(Entity entity) { - ParentComponent& parentComponent = entity.GetComponent(); - if (parentComponent.HasParent) { // Remove self from parents children lists. - int idx = 0; - ParentComponent& parentParentComponent = parentComponent.Parent.GetComponent(); + ParentComponent& parentC = entity.GetComponent(); + std::vector copyChildrens = parentC.Children; + if (parentC.HasParent) { // Remove self from parents children lists. + ParentComponent& parent = parentC.Parent.GetComponent(); + parent.RemoveChildren(entity); } - for (auto& c : parentComponent.Children) { + for (auto& c : copyChildrens) { + Logger::Log("Deleting... entity" + std::to_string(c.GetHandle())); DestroyEntity(c); } + + Logger::Log("Deleted entity" + std::to_string(entity.GetHandle()) + " - " + entity.GetComponent().Name); entity.Destroy(); + m_Registry.shrink_to_fit(); } // Getter @@ -515,8 +537,29 @@ bool Scene::Deserialize(const std::string& str) Entity ent = CreateEmptyEntity(); ent.Deserialize(e.dump()); } + + auto view = m_Registry.view(); + for (auto e : view) { + auto parentC = view.get(e); + if (!parentC.HasParent) + continue; + auto& p = Entity{ e, this }; + + auto parent = GetEntityByID(parentC.ParentID); + parent.AddChild(p); + } } } return true; } + +void Scene::Snapshot() +{ + const auto view = m_Registry.view(); + +} + +void Scene::LoadSnapshot() +{ +} diff --git a/Nuake/src/Scene/Scene.h b/Nuake/src/Scene/Scene.h index 06c5edcd..a0273362 100644 --- a/Nuake/src/Scene/Scene.h +++ b/Nuake/src/Scene/Scene.h @@ -39,6 +39,7 @@ public: std::string GetName(); bool SetName(std::string& newName); Entity GetEntity(int handle); + Entity GetEntityByID(int id); void Init(); void OnInit(); void OnExit(); @@ -77,4 +78,7 @@ public: json Serialize() override; bool Deserialize(const std::string& str) override; + + void Snapshot(); + void LoadSnapshot(); }; \ No newline at end of file diff --git a/Nuake/src/Scene/SceneArchive.h b/Nuake/src/Scene/SceneArchive.h new file mode 100644 index 00000000..72b18f0e --- /dev/null +++ b/Nuake/src/Scene/SceneArchive.h @@ -0,0 +1,21 @@ +#pragma once +#include + +class SceneArchive { +private: + +public: + void operator()(entt::entity); + + void operator()(std::underlying_type_t); + + template + void operator()(entt::entity, const T&); + + void operator()(entt::entity&); + + void operator()(std::underlying_type_t&); + + template + void operator()(entt::entity&, T&); +}; \ No newline at end of file diff --git a/Nuake/src/Scene/Systems/QuakeMapBuilder.cpp b/Nuake/src/Scene/Systems/QuakeMapBuilder.cpp index 5d18eefc..01db11e9 100644 --- a/Nuake/src/Scene/Systems/QuakeMapBuilder.cpp +++ b/Nuake/src/Scene/Systems/QuakeMapBuilder.cpp @@ -274,14 +274,15 @@ void QuakeMapBuilder::BuildQuakeMap(Entity& ent, bool Collisions) QuakeMapComponent& quakeMapC = ent.GetComponent(); Scene* m_Scene = ent.GetScene(); - // Clear old map entities. + // TODO: Tag entities *owned* by the map. ParentComponent& currentParent = ent.GetComponent(); - for (auto& e : currentParent.Children) { + + // Copy queue, cant delete while iterating. + auto deletionQueue = currentParent.Children; + for (auto& e : deletionQueue) { m_Scene->DestroyEntity(e); } - currentParent.Children.clear(); - map_parser_load(std::string(FileSystem::Root + quakeMapC.Path).c_str()); geo_generator_run(); diff --git a/Nuake/src/Scene/Systems/ScriptingSystem.cpp b/Nuake/src/Scene/Systems/ScriptingSystem.cpp index e8f9182d..e873a2c8 100644 --- a/Nuake/src/Scene/Systems/ScriptingSystem.cpp +++ b/Nuake/src/Scene/Systems/ScriptingSystem.cpp @@ -7,13 +7,12 @@ ScriptingSystem::ScriptingSystem(Scene* scene) m_Scene = scene; } - void ScriptingSystem::Init() { ScriptingEngine::Init(); auto entities = m_Scene->m_Registry.view(); - for (auto& e : entities) + for (auto e : entities) { WrenScriptComponent& wren = entities.get(e); if (wren.Script != "" && wren.Class != "") diff --git a/Nuake/src/Vendors/entt/entt.hpp b/Nuake/src/Vendors/entt/entt.hpp index b085a2d5..f089a319 100644 --- a/Nuake/src/Vendors/entt/entt.hpp +++ b/Nuake/src/Vendors/entt/entt.hpp @@ -1,3 +1,15 @@ +// #include "config/version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 7 +#define ENTT_VERSION_PATCH 1 + + +#endif + // #include "core/algorithm.hpp" #ifndef ENTT_CORE_ALGORITHM_HPP #define ENTT_CORE_ALGORITHM_HPP @@ -24,16 +36,6 @@ #endif -#ifndef ENTT_HS_SUFFIX -# define ENTT_HS_SUFFIX _hs -#endif - - -#ifndef ENTT_HWS_SUFFIX -# define ENTT_HWS_SUFFIX _hws -#endif - - #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else @@ -48,12 +50,17 @@ #endif -#ifndef ENTT_PAGE_SIZE -# define ENTT_PAGE_SIZE 32768 +#ifdef ENTT_PAGE_SIZE +static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); +#else +# define ENTT_PAGE_SIZE 4096 #endif -#ifndef ENTT_ASSERT +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition) assert(condition) #endif @@ -69,17 +76,11 @@ #ifndef ENTT_STANDARD_CPP -# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) -# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' -# elif 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_CONSTEXPR +# elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' @@ -87,13 +88,6 @@ #endif -#ifndef ENTT_STANDALONE -# define ENTT_FAST_PATH(...) false -#else -# define ENTT_FAST_PATH(Cond) Cond -#endif - - #endif @@ -152,7 +146,8 @@ struct overloaded: Func... { * @tparam Func Types of function objects. */ template -overloaded(Func...) -> overloaded; +overloaded(Func...) +-> overloaded; /** @@ -246,7 +241,7 @@ struct insertion_sort { template> void operator()(It first, It last, Compare compare = Compare{}) const { if(first < last) { - for(auto it = first+1; it < last; ++it) { + for(auto it = first + 1; it < last; ++it) { auto value = std::move(*it); auto pre = it; @@ -299,17 +294,17 @@ struct radix_sort { std::size_t index[buckets]{}; std::size_t count[buckets]{}; - std::for_each(from, to, [&getter, &count, start](const value_type &item) { - ++count[(getter(item) >> start) & mask]; - }); + for(auto it = from; it != to; ++it) { + ++count[(getter(*it) >> start) & mask]; + } - std::for_each(std::next(std::begin(index)), std::end(index), [index = std::begin(index), count = std::begin(count)](auto &item) mutable { - item = *(index++) + *(count++); - }); + for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) { + index[pos + 1u] = index[pos] + count[pos]; + } - std::for_each(from, to, [&getter, &out, &index, start](value_type &item) { - out[index[(getter(item) >> start) & mask]++] = std::move(item); - }); + 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) { @@ -329,6 +324,1711 @@ struct radix_sort { } +#endif + +// #include "core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +#include +// #include "../config/config.h" + + + +namespace entt { + + +template)> +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; + + +} + + +#endif + +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + + +#include +#include +// #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 "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +struct fnv1a_traits; + + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string { + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *curr) ENTT_NOEXCEPT: str{curr} {} + const Char *str; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT { + auto value = traits_type::offset; + + while(*curr != 0) { + value = (value ^ static_cast(*(curr++))) * traits_type::prime; + } + + return value; + } + +public: + /*! @brief Character type. */ + using value_type = Char; + /*! @brief Unsigned integer type. */ + using hash_type = id_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { + id_type partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + + /** + * @brief Returns directly the numeric representation of a string. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * const auto value = basic_hashed_string::to_value("my.png"); + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + return helper(str); + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { + return helper(wrapper.str); + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_NOEXCEPT + : str{nullptr}, hash{} + {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * basic_hashed_string hs{"my.png"}; + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param curr Human-readable identifer. + */ + template + constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT + : str{curr}, hash{helper(curr)} + {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + : str{wrapper.str}, hash{helper(wrapper.str)} + {} + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the instance. + */ + [[nodiscard]] constexpr const value_type * data() const ENTT_NOEXCEPT { + return str; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { + return hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { return data(); } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } + + /** + * @brief Compares two hashed strings. + * @param other Hashed string with which to compare. + * @return True if the two hashed strings are identical, false otherwise. + */ + [[nodiscard]] constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { + return hash == other.hash; + } + +private: + const value_type *str; + hash_type hash; +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the character type of the hashed string directly from a + * human-readable identifer provided to the constructor. + * + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + */ +template +basic_hashed_string(const Char (&str)[N]) +-> basic_hashed_string; + + +/** + * @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 +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + + +inline namespace literals { + + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_string{str}; +} + + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_wstring{str}; +} + + +} + + +} + + +#endif + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct ENTT_API type_seq final { + [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + + +template +[[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().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + + +template +[[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()); + return value; +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_seq final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() ENTT_NOEXCEPT { + static const id_type value = internal::type_seq::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } +}; + + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +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(0); +#else + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_seq::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 +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(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } +}; + + +/*! @brief Implementation specific information about a type. */ +class type_info final { + template + friend type_info type_id() ENTT_NOEXCEPT; + + type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT + : seq_value{seq_v}, + hash_value{hash_v}, + name_value{name_v} + {} + +public: + /*! @brief Default constructor. */ + type_info() ENTT_NOEXCEPT + : type_info({}, {}, {}) + {} + + /*! @brief Default copy constructor. */ + type_info(const type_info &) ENTT_NOEXCEPT = default; + /*! @brief Default move constructor. */ + type_info(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Default copy assignment operator. + * @return This type info object. + */ + type_info & operator=(const type_info &) ENTT_NOEXCEPT = default; + + /** + * @brief Default move assignment operator. + * @return This type info object. + */ + type_info & operator=(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Checks if a type info object is properly initialized. + * @return True if the object is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return name_value.data() != nullptr; + } + + /** + * @brief Type sequential identifier. + * @return Type sequential identifier. + */ + [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { + return seq_value; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { + return hash_value; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { + return name_value; + } + + /** + * @brief Compares the contents of two type info objects. + * @param other Object with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const type_info &other) const ENTT_NOEXCEPT { + return hash_value == other.hash_value; + } + +private: + id_type seq_value; + id_type hash_value; + std::string_view name_value; +}; + + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two contents differ, false otherwise. + */ +[[nodiscard]] inline bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/** + * @brief Returns the type info object for a given type. + * @tparam Type Type for which to generate a type info object. + * @return The type info object for the given type. + */ +template +[[nodiscard]] type_info type_id() ENTT_NOEXCEPT { + return type_info{ + type_seq>>::value(), + type_hash>>::value(), + type_name>>::value() + }; +} + + +} + + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // Unfortunately, doxygen cannot parse such a construct. + /*! @cond TURN_OFF_DOXYGEN */ + : choice_t + /*! @endcond */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t 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 +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::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 +struct size_of: std::integral_constant {}; + + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr auto size_of_v = size_of::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 +using unpack_as_t = Type; + + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_v = Value; + + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +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 +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 +struct type_list_element> + : type_list_element> +{}; + + +/** + * @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 +struct type_list_element<0u, type_list> { + /*! @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 +using type_list_element_t = typename type_list_element::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 +constexpr type_list operator+(type_list, type_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + std::disjunction_v...>, + typename type_list_unique>::type, + type_list_cat_t, typename type_list_unique>::type> + >; +}; + + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = type_list<>; +}; + + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::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 +struct type_list_contains; + + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other>: std::disjunction...> {}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr auto type_list_contains_v = type_list_contains::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +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 +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 +struct value_list_element> + : value_list_element> +{}; + + +/** + * @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 +struct value_list_element<0u, value_list> { + /*! @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 +inline constexpr auto value_list_element_v = value_list_element::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 +constexpr value_list operator+(value_list, value_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +[[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) +-> decltype(std::declval() == std::declval()) { return true; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>); +} + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template +struct is_equality_comparable: std::bool_constant(choice<2>)> {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** +* @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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr auto is_applicable_v = is_applicable::value; + + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +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 +struct is_applicable_r>: std::is_invocable_r {}; + + +/** + * @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 +inline constexpr auto is_applicable_r_v = is_applicable_r::value; + + +/** +* @brief Provides the member constant `value` to true if a given type is +* complete, false otherwise. +* @tparam Type Potential complete type. +*/ +template +struct is_complete: std::false_type {}; + + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + + +/** +* @brief Helper variable template. +* @tparam Type Potential complete type. +*/ +template +inline constexpr auto is_complete_v = is_complete::value; + + +/** + * @brief Provides the member constant `value` to true if a given type is empty + * and the empty type optimization is enabled, false otherwise. + * @tparam Type Potential empty type. + */ +template +struct is_empty: ENTT_IS_EMPTY(Type) {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potential empty type. + */ +template +inline constexpr auto is_empty_v = is_empty::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 +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::add_const_t; +}; + + +/** + * @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 +using constness_as_t = typename constness_as::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 +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class * clazz(Ret(Class:: *)(Args...)); + + template + static Class * clazz(Ret(Class:: *)(Args...) const); + + template + static Class * clazz(Type Class:: *); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + + +} + + +#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 +class basic_any { + enum class operation { COPY, MOVE, DTOR, COMP, ADDR, CADDR, REF, CREF, TYPE }; + + using storage_type = std::aligned_storage_t; + using vtable_type = const void *(const operation, const basic_any &, const void *); + + template + static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; + + template + [[nodiscard]] static bool compare(const void *lhs, const void *rhs) { + if constexpr(!std::is_function_v && is_equality_comparable_v) { + return *static_cast(lhs) == *static_cast(rhs); + } else { + return lhs == rhs; + } + } + + template + static Type & as(const void *to) { + return *const_cast(static_cast(to)); + } + + template + static const void * basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &from, [[maybe_unused]] const void *to) { + if constexpr(std::is_void_v) { + switch(op) { + case operation::COPY: + case operation::MOVE: + case operation::REF: + case operation::CREF: + as(to).vtable = from.vtable; + break; + default: + break; + } + } else if constexpr(std::is_lvalue_reference_v) { + using base_type = std::decay_t; + + switch(op) { + case operation::COPY: + if constexpr(std::is_copy_constructible_v) { + as(to) = *static_cast(from.instance); + } + break; + case operation::MOVE: + as(to).instance = from.instance; + as(to).vtable = from.vtable; + [[fallthrough]]; + case operation::DTOR: + break; + case operation::COMP: + return compare(from.instance, to) ? to : nullptr; + case operation::ADDR: + return std::is_const_v> ? nullptr : from.instance; + case operation::CADDR: + return from.instance; + case operation::REF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::CREF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::TYPE: + as(to) = type_id(); + break; + } + } else if constexpr(in_situ) { + #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L + auto *instance = const_cast(std::launder(reinterpret_cast(&from.storage))); + #else + auto *instance = const_cast(reinterpret_cast(&from.storage)); + #endif + + switch(op) { + case operation::COPY: + if constexpr(std::is_copy_constructible_v) { + new (&as(to).storage) Type{std::as_const(*instance)}; + as(to).vtable = from.vtable; + } + break; + case operation::MOVE: + new (&as(to).storage) Type{std::move(*instance)}; + as(to).vtable = from.vtable; + break; + case operation::DTOR: + instance->~Type(); + break; + case operation::COMP: + return compare(instance, to) ? to : nullptr; + case operation::ADDR: + case operation::CADDR: + return instance; + case operation::REF: + as(to).instance = instance; + as(to).vtable = basic_vtable; + break; + case operation::CREF: + as(to).instance = instance; + as(to).vtable = basic_vtable; + break; + case operation::TYPE: + as(to) = type_id(); + break; + } + } else { + switch(op) { + case operation::COPY: + if constexpr(std::is_copy_constructible_v) { + as(to).instance = new Type{*static_cast(from.instance)}; + as(to).vtable = from.vtable; + } + break; + case operation::MOVE: + as(to).instance = std::exchange(as(&from).instance, nullptr); + as(to).vtable = from.vtable; + break; + case operation::DTOR: + if constexpr(std::is_array_v) { + delete[] static_cast(from.instance); + } else { + delete static_cast(from.instance); + } + break; + case operation::COMP: + return compare(from.instance, to) ? to : nullptr; + case operation::ADDR: + case operation::CADDR: + return from.instance; + case operation::REF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::CREF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::TYPE: + as(to) = type_id(); + break; + } + } + + return nullptr; + } + + template + void initialize([[maybe_unused]] Args &&... args) { + if constexpr(!std::is_void_v) { + if constexpr(std::is_lvalue_reference_v) { + static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); + instance = (std::addressof(args), ...); + } else if constexpr(in_situ) { + if constexpr(std::is_aggregate_v) { + new (&storage) Type{std::forward(args)...}; + } else { + new (&storage) Type(std::forward(args)...); + } + } else { + if constexpr(std::is_aggregate_v) { + instance = new Type{std::forward(args)...}; + } else { + instance = new Type(std::forward(args)...); + } + } + } + } + +public: + /*! @brief Default constructor. */ + basic_any() ENTT_NOEXCEPT + : basic_any{std::in_place_type} + {} + + /** + * @brief Constructs an any by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&... args) + : instance{}, + vtable{&basic_vtable} + { + initialize(std::forward(args)...); + } + + /** + * @brief Constructs an any that holds an unmanaged object. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template + basic_any(std::reference_wrapper value) ENTT_NOEXCEPT + : basic_any{std::in_place_type, value.get()} + {} + + /** + * @brief Constructs an any from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, basic_any>>> + basic_any(Type &&value) + : basic_any{std::in_place_type>, std::forward(value)} + {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + basic_any(const basic_any &other) + : basic_any{std::in_place_type} + { + other.vtable(operation::COPY, other, this); + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_any(basic_any &&other) ENTT_NOEXCEPT + : basic_any{std::in_place_type} + { + other.vtable(operation::MOVE, other, this); + } + + /*! @brief Frees the internal storage, whatever it means. */ + ~basic_any() { + vtable(operation::DTOR, *this, nullptr); + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ + basic_any & operator=(const basic_any &other) { + vtable(operation::DTOR, *this, nullptr); + 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) { + vtable(operation::DTOR, *this, nullptr); + other.vtable(operation::MOVE, other, this); + 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 + basic_any & operator=(std::reference_wrapper value) ENTT_NOEXCEPT { + emplace(value.get()); + 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 + std::enable_if_t, basic_any>, basic_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns the type of the contained object. + * @return The type of the contained object, if any. + */ + [[nodiscard]] type_info type() const ENTT_NOEXCEPT { + type_info info{}; + vtable(operation::TYPE, *this, &info); + return info; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void * data() const ENTT_NOEXCEPT { + return vtable(operation::CADDR, *this, nullptr); + } + + /*! @copydoc data */ + [[nodiscard]] void * data() ENTT_NOEXCEPT { + return const_cast(vtable(operation::ADDR, *this, nullptr)); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + void emplace(Args &&... args) { + std::exchange(vtable, &basic_vtable)(operation::DTOR, *this, nullptr); + initialize(std::forward(args)...); + } + + /*! @brief Destroys contained object */ + void reset() { + std::exchange(vtable, &basic_vtable)(operation::DTOR, *this, nullptr); + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(vtable(operation::CADDR, *this, nullptr) == nullptr); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ + bool operator==(const basic_any &other) const ENTT_NOEXCEPT { + return type() == other.type() && (vtable(operation::COMP, *this, other.data()) == other.data()); + } + + /** + * @brief Aliasing constructor. + * @return An any that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT { + basic_any ref{}; + vtable(operation::REF, *this, &ref); + return ref; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT { + basic_any ref{}; + vtable(operation::CREF, *this, &ref); + return ref; + } + +private: + union { const void *instance; storage_type storage; }; + vtable_type *vtable; +}; + + +/** + * @brief Checks if two wrappers differ in their content. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param lhs A wrapper, either empty or not. + * @param rhs A wrapper, either empty or not. + * @return True if the two wrappers differ in their content, false otherwise. + */ +template +[[nodiscard]] inline bool operator!=(const basic_any &lhs, const basic_any &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 +Type any_cast(const basic_any &data) ENTT_NOEXCEPT { + const auto * const instance = any_cast>(&data); + ENTT_ASSERT(instance); + return static_cast(*instance); +} + + +/*! @copydoc any_cast */ +template +Type any_cast(basic_any &data) ENTT_NOEXCEPT { + // forces const on non-reference types to make them work also with wrappers for const references + auto * const instance = any_cast>(&data); + ENTT_ASSERT(instance); + return static_cast(*instance); +} + + +/*! @copydoc any_cast */ +template +Type any_cast(basic_any &&data) ENTT_NOEXCEPT { + // forces const on non-reference types to make them work also with wrappers for const references + auto * const instance = any_cast>(&data); + ENTT_ASSERT(instance); + return static_cast(std::move(*instance)); +} + + +/*! @copydoc any_cast */ +template +const Type * any_cast(const basic_any *data) ENTT_NOEXCEPT { + return (data->type() == type_id() ? static_cast(data->data()) : nullptr); +} + + +/*! @copydoc any_cast */ +template +Type * any_cast(basic_any *data) ENTT_NOEXCEPT { + // last attempt to make wrappers for const references return their values + return (data->type() == type_id() ? static_cast(static_cast, Type> *>(data)->data()) : nullptr); +} + + +} + + #endif // #include "core/attribute.h" @@ -374,25 +2074,6 @@ struct radix_sort { // #include "../config/config.h" // #include "fwd.hpp" -#ifndef ENTT_CORE_FWD_HPP -#define ENTT_CORE_FWD_HPP - - -// #include "../config/config.h" - - - -namespace entt { - - -/*! @brief Alias declaration for type identifiers. */ -using id_type = ENTT_ID_TYPE; - - -} - - -#endif @@ -518,6 +2199,18 @@ public: /*! @brief Unsigned integer type. */ using hash_type = id_type; + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { + id_type partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + /** * @brief Returns directly the numeric representation of a string. * @@ -547,18 +2240,6 @@ public: return helper(wrapper.str); } - /** - * @brief Returns directly the numeric representation of a string view. - * @param str Human-readable identifer. - * @param size Length of the string to hash. - * @return The numeric representation of the string. - */ - [[nodiscard]] static hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { - id_type partial{traits_type::offset}; - while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } - return partial; - } - /*! @brief Constructs an empty hashed string. */ constexpr basic_hashed_string() ENTT_NOEXCEPT : str{nullptr}, hash{} @@ -643,7 +2324,7 @@ private: * @param str Human-readable identifer. */ template -basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; @@ -668,7 +2349,7 @@ using hashed_string = basic_hashed_string; using hashed_wstring = basic_hashed_string; -} +inline namespace literals { /** @@ -676,7 +2357,7 @@ using hashed_wstring = basic_hashed_string; * @param str The literal without its suffix. * @return A properly initialized hashed string. */ -[[nodiscard]] constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT { +[[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_string{str}; } @@ -686,11 +2367,17 @@ using hashed_wstring = basic_hashed_string; * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ -[[nodiscard]] constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { +[[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_wstring{str}; } +} + + +} + + #endif // #include "core/ident.hpp" @@ -698,7 +2385,6 @@ using hashed_wstring = basic_hashed_string; #define ENTT_CORE_IDENT_HPP -#include #include #include #include @@ -706,6 +2392,8 @@ using hashed_wstring = basic_hashed_string; // #include "fwd.hpp" +// #include "type_traits.hpp" + namespace entt { @@ -740,12 +2428,10 @@ namespace entt { */ template class identifier { - using tuple_type = std::tuple...>; - - template - [[nodiscard]] static constexpr id_type get(std::index_sequence) { + template + [[nodiscard]] static constexpr id_type get(std::index_sequence) { static_assert(std::disjunction_v...>, "Invalid type"); - return (0 + ... + (std::is_same_v> ? id_type(Indexes) : id_type{})); + return (0 + ... + (std::is_same_v...>>> ? id_type{Index} : id_type{})); } public: @@ -835,42 +2521,10 @@ inline monostate monostate_v = {}; #include +#include // #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 "hashed_string.hpp" @@ -890,7 +2544,7 @@ namespace entt { namespace internal { -struct ENTT_API type_index { +struct ENTT_API type_seq final { [[nodiscard]] static id_type next() ENTT_NOEXCEPT { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; @@ -899,18 +2553,49 @@ struct ENTT_API type_index { template -[[nodiscard]] constexpr auto type_name() ENTT_NOEXCEPT { +[[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{}; + return std::string_view{""}; #endif } +template().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + + +template +[[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()); + return value; +} + + } @@ -921,92 +2606,176 @@ template /** - * @brief Type index. + * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template -struct ENTT_API type_index { +struct ENTT_API type_seq final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() ENTT_NOEXCEPT { - static const id_type value = internal::type_index::next(); + static const id_type value = internal::type_seq::next(); return value; } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** - * @brief Provides the member constant `value` to true if a given type is - * indexable, false otherwise. - * @tparam Type Potentially indexable type. - */ -template -struct has_type_index: std::false_type {}; - - -/*! @brief has_type_index */ -template -struct has_type_index::value())>>: std::true_type {}; - - -/** - * @brief Helper variable template. - * @tparam Type Potentially indexable type. - */ -template -inline constexpr bool has_type_index_v = has_type_index::value; - - -/** - * @brief Type info. - * @tparam Type Type for which to generate information. + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. */ template -struct type_info { +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_CONSTEXPR - [[nodiscard]] static constexpr id_type id() ENTT_NOEXCEPT { - constexpr auto value = hashed_string::value(ENTT_PRETTY_FUNCTION); - return value; - } -#elif defined ENTT_PRETTY_FUNCTION - [[nodiscard]] static id_type id() ENTT_NOEXCEPT { - static const auto value = hashed_string::value(ENTT_PRETTY_FUNCTION); - return value; - } +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return internal::type_hash(0); #else - [[nodiscard]] static id_type id() ENTT_NOEXCEPT { - return type_index::value(); - } + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_seq::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 +struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ -#if defined ENTT_PRETTY_FUNCTION_CONSTEXPR - [[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT { - constexpr auto value = internal::type_name(); - return value; + [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { + return internal::type_name(0); } -#elif defined ENTT_PRETTY_FUNCTION - [[nodiscard]] static std::string_view name() ENTT_NOEXCEPT { - static const auto value = internal::type_name(); - return value; - } -#else - [[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT { - return internal::type_name(); - } -#endif + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } }; +/*! @brief Implementation specific information about a type. */ +class type_info final { + template + friend type_info type_id() ENTT_NOEXCEPT; + + type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT + : seq_value{seq_v}, + hash_value{hash_v}, + name_value{name_v} + {} + +public: + /*! @brief Default constructor. */ + type_info() ENTT_NOEXCEPT + : type_info({}, {}, {}) + {} + + /*! @brief Default copy constructor. */ + type_info(const type_info &) ENTT_NOEXCEPT = default; + /*! @brief Default move constructor. */ + type_info(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Default copy assignment operator. + * @return This type info object. + */ + type_info & operator=(const type_info &) ENTT_NOEXCEPT = default; + + /** + * @brief Default move assignment operator. + * @return This type info object. + */ + type_info & operator=(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Checks if a type info object is properly initialized. + * @return True if the object is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return name_value.data() != nullptr; + } + + /** + * @brief Type sequential identifier. + * @return Type sequential identifier. + */ + [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { + return seq_value; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { + return hash_value; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { + return name_value; + } + + /** + * @brief Compares the contents of two type info objects. + * @param other Object with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const type_info &other) const ENTT_NOEXCEPT { + return hash_value == other.hash_value; + } + +private: + id_type seq_value; + id_type hash_value; + std::string_view name_value; +}; + + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two contents differ, false otherwise. + */ +[[nodiscard]] inline bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/** + * @brief Returns the type info object for a given type. + * @tparam Type Type for which to generate a type info object. + * @return The type info object for the given type. + */ +template +[[nodiscard]] type_info type_id() ENTT_NOEXCEPT { + return type_info{ + type_seq>>::value(), + type_hash>>::value(), + type_name>>::value() + }; +} + + } @@ -1029,6 +2798,79 @@ struct type_info { namespace entt { +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // Unfortunately, doxygen cannot parse such a construct. + /*! @cond TURN_OFF_DOXYGEN */ + : choice_t + /*! @endcond */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t 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 +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::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 +struct size_of: std::integral_constant {}; + + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr auto size_of_v = size_of::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. @@ -1056,7 +2898,7 @@ using integral_constant = std::integral_constant; /** - * @brief Alias template to ease the creation of named values. + * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template @@ -1064,57 +2906,64 @@ using tag = integral_constant; /** - * @brief Utility class to disambiguate overloaded functions. - * @tparam N Number of choices available. - */ -template -struct choice_t - // Unfortunately, doxygen cannot parse such a construct. - /*! @cond TURN_OFF_DOXYGEN */ - : choice_t - /*! @endcond */ -{}; - - -/*! @copybrief choice_t */ -template<> -struct choice_t<0> {}; - - -/** - * @brief Variable template for the choice trick. - * @tparam N Number of choices available. - */ -template -inline constexpr choice_t choice{}; - - -/*! @brief A class to use to push around lists of types, nothing more. */ -template -struct type_list {}; - - -/*! @brief Primary template isn't defined on purpose. */ -template -struct type_list_size; - - -/** - * @brief Compile-time number of elements in a type list. + * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template -struct type_list_size> - : std::integral_constant +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 +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 +struct type_list_element> + : type_list_element> {}; /** - * @brief Helper variable template. - * @tparam List Type list. + * @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 -inline constexpr auto type_list_size_v = type_list_size::value; +template +struct type_list_element<0u, type_list> { + /*! @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 +using type_list_element_t = typename type_list_element::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 +constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ @@ -1199,20 +3048,213 @@ template using type_list_unique_t = typename type_list_unique::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 +struct type_list_contains; + + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other>: std::disjunction...> {}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr auto type_list_contains_v = type_list_contains::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +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 +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 +struct value_list_element> + : value_list_element> +{}; + + +/** + * @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 +struct value_list_element<0u, value_list> { + /*! @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 +inline constexpr auto value_list_element_v = value_list_element::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 +constexpr value_list operator+(value_list, value_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +[[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) +-> decltype(std::declval() == std::declval()) { return true; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>); +} + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type Potentially equality comparable type. */ -template> -struct is_equality_comparable: std::false_type {}; - - -/*! @copydoc is_equality_comparable */ -template -struct is_equality_comparable() == std::declval())>> - : std::true_type -{}; +template +struct is_equality_comparable: std::bool_constant(choice<2>)> {}; /** @@ -1223,15 +3265,96 @@ template inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; +/*! @brief Same as std::is_invocable, but with tuples. */ +template +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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** +* @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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr auto is_applicable_v = is_applicable::value; + + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +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 +struct is_applicable_r>: std::is_invocable_r {}; + + +/** + * @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 +inline constexpr auto is_applicable_r_v = is_applicable_r::value; + + +/** +* @brief Provides the member constant `value` to true if a given type is +* complete, false otherwise. +* @tparam Type Potential complete type. +*/ +template +struct is_complete: std::false_type {}; + + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + + +/** +* @brief Helper variable template. +* @tparam Type Potential complete type. +*/ +template +inline constexpr auto is_complete_v = is_complete::value; + + /** * @brief Provides the member constant `value` to true if a given type is empty * and the empty type optimization is enabled, false otherwise. * @tparam Type Potential empty type. */ template -struct is_eto_eligible - : ENTT_IS_EMPTY(Type) -{}; +struct is_empty: ENTT_IS_EMPTY(Type) {}; /** @@ -1239,7 +3362,36 @@ struct is_eto_eligible * @tparam Type Potential empty type. */ template -inline constexpr auto is_eto_eligible_v = is_eto_eligible::value; +inline constexpr auto is_empty_v = is_empty::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 +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::add_const_t; +}; + + +/** + * @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 +using constness_as_t = typename constness_as::type; /** @@ -1279,208 +3431,12 @@ using member_class_t = typename member_class::type; #endif // #include "core/utility.hpp" - -// #include "entity/actor.hpp" -#ifndef ENTT_ENTITY_ACTOR_HPP -#define ENTT_ENTITY_ACTOR_HPP - - -#include -#include -// #include "../config/config.h" -#ifndef ENTT_CONFIG_CONFIG_H -#define ENTT_CONFIG_CONFIG_H - - -#ifndef ENTT_NOEXCEPT -# define ENTT_NOEXCEPT noexcept -#endif - - -#ifndef ENTT_HS_SUFFIX -# define ENTT_HS_SUFFIX _hs -#endif - - -#ifndef ENTT_HWS_SUFFIX -# define ENTT_HWS_SUFFIX _hws -#endif - - -#ifndef ENTT_USE_ATOMIC -# define ENTT_MAYBE_ATOMIC(Type) Type -#else -# include -# define ENTT_MAYBE_ATOMIC(Type) std::atomic -#endif - - -#ifndef ENTT_ID_TYPE -# include -# define ENTT_ID_TYPE std::uint32_t -#endif - - -#ifndef ENTT_PAGE_SIZE -# define ENTT_PAGE_SIZE 32768 -#endif - - -#ifndef ENTT_ASSERT -# include -# define ENTT_ASSERT(condition) assert(condition) -#endif - - -#ifndef ENTT_NO_ETO -# include -# define ENTT_IS_EMPTY(Type) std::is_empty -#else -# include -# define ENTT_IS_EMPTY(Type) std::false_type -#endif - - -#ifndef ENTT_STANDARD_CPP -# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) -# define ENTT_PRETTY_FUNCTION_CONSTEXPR -# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ -# define ENTT_PRETTY_FUNCTION_PREFIX '=' -# define ENTT_PRETTY_FUNCTION_SUFFIX ']' -# elif 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_CONSTEXPR -# define ENTT_PRETTY_FUNCTION __FUNCSIG__ -# define ENTT_PRETTY_FUNCTION_PREFIX '<' -# define ENTT_PRETTY_FUNCTION_SUFFIX '>' -# endif -#endif - - -#ifndef ENTT_STANDALONE -# define ENTT_FAST_PATH(...) false -#else -# define ENTT_FAST_PATH(Cond) Cond -#endif - - -#endif - -// #include "registry.hpp" -#ifndef ENTT_ENTITY_REGISTRY_HPP -#define ENTT_ENTITY_REGISTRY_HPP - - -#include -#include -#include -#include -#include -#include -#include -#include -// #include "../config/config.h" - -// #include "../core/algorithm.hpp" -#ifndef ENTT_CORE_ALGORITHM_HPP -#define ENTT_CORE_ALGORITHM_HPP - - -#include -#include -#include -#include -#include -// #include "utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include // #include "../config/config.h" -#ifndef ENTT_CONFIG_CONFIG_H -#define ENTT_CONFIG_CONFIG_H - - -#ifndef ENTT_NOEXCEPT -# define ENTT_NOEXCEPT noexcept -#endif - - -#ifndef ENTT_HS_SUFFIX -# define ENTT_HS_SUFFIX _hs -#endif - - -#ifndef ENTT_HWS_SUFFIX -# define ENTT_HWS_SUFFIX _hws -#endif - - -#ifndef ENTT_USE_ATOMIC -# define ENTT_MAYBE_ATOMIC(Type) Type -#else -# include -# define ENTT_MAYBE_ATOMIC(Type) std::atomic -#endif - - -#ifndef ENTT_ID_TYPE -# include -# define ENTT_ID_TYPE std::uint32_t -#endif - - -#ifndef ENTT_PAGE_SIZE -# define ENTT_PAGE_SIZE 32768 -#endif - - -#ifndef ENTT_ASSERT -# include -# define ENTT_ASSERT(condition) assert(condition) -#endif - - -#ifndef ENTT_NO_ETO -# include -# define ENTT_IS_EMPTY(Type) std::is_empty -#else -# include -# define ENTT_IS_EMPTY(Type) std::false_type -#endif - - -#ifndef ENTT_STANDARD_CPP -# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) -# define ENTT_PRETTY_FUNCTION_CONSTEXPR -# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ -# define ENTT_PRETTY_FUNCTION_PREFIX '=' -# define ENTT_PRETTY_FUNCTION_SUFFIX ']' -# elif 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_CONSTEXPR -# define ENTT_PRETTY_FUNCTION __FUNCSIG__ -# define ENTT_PRETTY_FUNCTION_PREFIX '<' -# define ENTT_PRETTY_FUNCTION_SUFFIX '>' -# endif -#endif - - -#ifndef ENTT_STANDALONE -# define ENTT_FAST_PATH(...) false -#else -# define ENTT_FAST_PATH(Cond) Cond -#endif - - -#endif @@ -1538,7 +3494,1524 @@ struct overloaded: Func... { * @tparam Func Types of function objects. */ template -overloaded(Func...) -> overloaded; +overloaded(Func...) +-> overloaded; + + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +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 + decltype(auto) operator()(Args &&... args) const { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + decltype(auto) operator()(Args &&... args) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + + +} + + +#endif + +// #include "entity/entity.hpp" +#ifndef ENTT_ENTITY_ENTITY_HPP +#define ENTT_ENTITY_ENTITY_HPP + + +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifdef ENTT_PAGE_SIZE +static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); +#else +# define ENTT_PAGE_SIZE 4096 +#endif + + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#endif + + + +namespace entt { + + +/** + * @brief Entity traits. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is an accepted entity type. + */ +template +struct entt_traits; + + +/** + * @brief Entity traits for enumeration types. + * @tparam Type The type to check. + */ +template +struct entt_traits>> + : entt_traits> +{}; + + +/** + * @brief Entity traits for a 32 bits entity identifier. + * + * A 32 bits entity identifier guarantees: + * + * * 20 bits for the entity number (suitable for almost all the games). + * * 12 bit for the version (resets in [0-4095]). + */ +template<> +struct entt_traits { + /*! @brief Underlying entity type. */ + using entity_type = std::uint32_t; + /*! @brief Underlying version type. */ + using version_type = std::uint16_t; + /*! @brief Difference type. */ + using difference_type = std::int64_t; + + /*! @brief Mask to use to get the entity number out of an identifier. */ + static constexpr entity_type entity_mask = 0xFFFFF; + /*! @brief Mask to use to get the version out of an identifier. */ + static constexpr entity_type version_mask = 0xFFF; + /*! @brief Extent of the entity number within an identifier. */ + static constexpr std::size_t entity_shift = 20u; +}; + + +/** + * @brief Entity traits for a 64 bits entity identifier. + * + * A 64 bits entity identifier guarantees: + * + * * 32 bits for the entity number (an indecently large number). + * * 32 bit for the version (an indecently large number). + */ +template<> +struct entt_traits { + /*! @brief Underlying entity type. */ + using entity_type = std::uint64_t; + /*! @brief Underlying version type. */ + using version_type = std::uint32_t; + /*! @brief Difference type. */ + using difference_type = std::int64_t; + + /*! @brief Mask to use to get the entity number out of an identifier. */ + static constexpr entity_type entity_mask = 0xFFFFFFFF; + /*! @brief Mask to use to get the version out of an identifier. */ + static constexpr entity_type version_mask = 0xFFFFFFFF; + /*! @brief Extent of the entity number within an identifier. */ + static constexpr std::size_t entity_shift = 32u; +}; + + +/** + * @brief Converts an entity type to its underlying type. + * @tparam Entity The value type. + * @param entity The value to convert. + * @return The integral representation of the given value. + */ +template +[[nodiscard]] constexpr auto to_integral(const Entity entity) ENTT_NOEXCEPT { + return static_cast::entity_type>(entity); +} + + +/*! @brief Null object for all entity identifiers. */ +struct null_t { + /** + * @brief Converts the null object to identifiers of any type. + * @tparam Entity Type of entity identifier. + * @return The null representation for the given identifier. + */ + template + [[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT { + return Entity{entt_traits::entity_mask}; + } + + /** + * @brief Compares two null objects. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==(const null_t &) const ENTT_NOEXCEPT { + return true; + } + + /** + * @brief Compares two null objects. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=(const null_t &) const ENTT_NOEXCEPT { + return false; + } + + /** + * @brief Compares a null object and an entity identifier of any type. + * @tparam Entity Type of entity identifier. + * @param entity Entity identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template + [[nodiscard]] constexpr bool operator==(const Entity &entity) const ENTT_NOEXCEPT { + return (to_integral(entity) & entt_traits::entity_mask) == to_integral(static_cast(*this)); + } + + /** + * @brief Compares a null object and an entity identifier of any type. + * @tparam Entity Type of entity identifier. + * @param entity Entity identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator!=(const Entity &entity) const ENTT_NOEXCEPT { + return !(entity == *this); + } +}; + + +/** + * @brief Compares a null object and an entity identifier of any type. + * @tparam Entity Type of entity identifier. + * @param entity Entity identifier with which to compare. + * @param other A null object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const Entity &entity, const null_t &other) ENTT_NOEXCEPT { + return other.operator==(entity); +} + + +/** + * @brief Compares a null object and an entity identifier of any type. + * @tparam Entity Type of entity identifier. + * @param entity Entity identifier with which to compare. + * @param other A null object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const Entity &entity, const null_t &other) ENTT_NOEXCEPT { + return !(other == entity); +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Compile-time constant for null entities. + * + * There exist implicit conversions from this variable to entity identifiers of + * any allowed type. Similarly, there exist comparision operators between the + * null entity and any other entity identifier. + */ +inline constexpr null_t null{}; + + +} + + +#endif + +// #include "entity/group.hpp" +#ifndef ENTT_ENTITY_GROUP_HPP +#define ENTT_ENTITY_GROUP_HPP + + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + + +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifdef ENTT_PAGE_SIZE +static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); +#else +# define ENTT_PAGE_SIZE 4096 +#endif + + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +#include +// #include "../config/config.h" + + + +namespace entt { + + +template)> +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; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // Unfortunately, doxygen cannot parse such a construct. + /*! @cond TURN_OFF_DOXYGEN */ + : choice_t + /*! @endcond */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t 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 +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::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 +struct size_of: std::integral_constant {}; + + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr auto size_of_v = size_of::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 +using unpack_as_t = Type; + + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_v = Value; + + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +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 +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 +struct type_list_element> + : type_list_element> +{}; + + +/** + * @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 +struct type_list_element<0u, type_list> { + /*! @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 +using type_list_element_t = typename type_list_element::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 +constexpr type_list operator+(type_list, type_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + std::disjunction_v...>, + typename type_list_unique>::type, + type_list_cat_t, typename type_list_unique>::type> + >; +}; + + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = type_list<>; +}; + + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::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 +struct type_list_contains; + + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other>: std::disjunction...> {}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr auto type_list_contains_v = type_list_contains::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +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 +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 +struct value_list_element> + : value_list_element> +{}; + + +/** + * @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 +struct value_list_element<0u, value_list> { + /*! @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 +inline constexpr auto value_list_element_v = value_list_element::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 +constexpr value_list operator+(value_list, value_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +[[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) +-> decltype(std::declval() == std::declval()) { return true; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>); +} + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template +struct is_equality_comparable: std::bool_constant(choice<2>)> {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** +* @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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr auto is_applicable_v = is_applicable::value; + + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +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 +struct is_applicable_r>: std::is_invocable_r {}; + + +/** + * @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 +inline constexpr auto is_applicable_r_v = is_applicable_r::value; + + +/** +* @brief Provides the member constant `value` to true if a given type is +* complete, false otherwise. +* @tparam Type Potential complete type. +*/ +template +struct is_complete: std::false_type {}; + + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + + +/** +* @brief Helper variable template. +* @tparam Type Potential complete type. +*/ +template +inline constexpr auto is_complete_v = is_complete::value; + + +/** + * @brief Provides the member constant `value` to true if a given type is empty + * and the empty type optimization is enabled, false otherwise. + * @tparam Type Potential empty type. + */ +template +struct is_empty: ENTT_IS_EMPTY(Type) {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potential empty type. + */ +template +inline constexpr auto is_empty_v = is_empty::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 +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::add_const_t; +}; + + +/** + * @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 +using constness_as_t = typename constness_as::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 +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class * clazz(Ret(Class:: *)(Args...)); + + template + static Class * clazz(Ret(Class:: *)(Args...) const); + + template + static Class * clazz(Type Class:: *); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + + +} + + +#endif + +// #include "entity.hpp" +#ifndef ENTT_ENTITY_ENTITY_HPP +#define ENTT_ENTITY_ENTITY_HPP + + +#include +#include +#include +// #include "../config/config.h" + + + +namespace entt { + + +/** + * @brief Entity traits. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is an accepted entity type. + */ +template +struct entt_traits; + + +/** + * @brief Entity traits for enumeration types. + * @tparam Type The type to check. + */ +template +struct entt_traits>> + : entt_traits> +{}; + + +/** + * @brief Entity traits for a 32 bits entity identifier. + * + * A 32 bits entity identifier guarantees: + * + * * 20 bits for the entity number (suitable for almost all the games). + * * 12 bit for the version (resets in [0-4095]). + */ +template<> +struct entt_traits { + /*! @brief Underlying entity type. */ + using entity_type = std::uint32_t; + /*! @brief Underlying version type. */ + using version_type = std::uint16_t; + /*! @brief Difference type. */ + using difference_type = std::int64_t; + + /*! @brief Mask to use to get the entity number out of an identifier. */ + static constexpr entity_type entity_mask = 0xFFFFF; + /*! @brief Mask to use to get the version out of an identifier. */ + static constexpr entity_type version_mask = 0xFFF; + /*! @brief Extent of the entity number within an identifier. */ + static constexpr std::size_t entity_shift = 20u; +}; + + +/** + * @brief Entity traits for a 64 bits entity identifier. + * + * A 64 bits entity identifier guarantees: + * + * * 32 bits for the entity number (an indecently large number). + * * 32 bit for the version (an indecently large number). + */ +template<> +struct entt_traits { + /*! @brief Underlying entity type. */ + using entity_type = std::uint64_t; + /*! @brief Underlying version type. */ + using version_type = std::uint32_t; + /*! @brief Difference type. */ + using difference_type = std::int64_t; + + /*! @brief Mask to use to get the entity number out of an identifier. */ + static constexpr entity_type entity_mask = 0xFFFFFFFF; + /*! @brief Mask to use to get the version out of an identifier. */ + static constexpr entity_type version_mask = 0xFFFFFFFF; + /*! @brief Extent of the entity number within an identifier. */ + static constexpr std::size_t entity_shift = 32u; +}; + + +/** + * @brief Converts an entity type to its underlying type. + * @tparam Entity The value type. + * @param entity The value to convert. + * @return The integral representation of the given value. + */ +template +[[nodiscard]] constexpr auto to_integral(const Entity entity) ENTT_NOEXCEPT { + return static_cast::entity_type>(entity); +} + + +/*! @brief Null object for all entity identifiers. */ +struct null_t { + /** + * @brief Converts the null object to identifiers of any type. + * @tparam Entity Type of entity identifier. + * @return The null representation for the given identifier. + */ + template + [[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT { + return Entity{entt_traits::entity_mask}; + } + + /** + * @brief Compares two null objects. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==(const null_t &) const ENTT_NOEXCEPT { + return true; + } + + /** + * @brief Compares two null objects. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=(const null_t &) const ENTT_NOEXCEPT { + return false; + } + + /** + * @brief Compares a null object and an entity identifier of any type. + * @tparam Entity Type of entity identifier. + * @param entity Entity identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template + [[nodiscard]] constexpr bool operator==(const Entity &entity) const ENTT_NOEXCEPT { + return (to_integral(entity) & entt_traits::entity_mask) == to_integral(static_cast(*this)); + } + + /** + * @brief Compares a null object and an entity identifier of any type. + * @tparam Entity Type of entity identifier. + * @param entity Entity identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator!=(const Entity &entity) const ENTT_NOEXCEPT { + return !(entity == *this); + } +}; + + +/** + * @brief Compares a null object and an entity identifier of any type. + * @tparam Entity Type of entity identifier. + * @param entity Entity identifier with which to compare. + * @param other A null object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const Entity &entity, const null_t &other) ENTT_NOEXCEPT { + return other.operator==(entity); +} + + +/** + * @brief Compares a null object and an entity identifier of any type. + * @tparam Entity Type of entity identifier. + * @param entity Entity identifier with which to compare. + * @param other A null object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const Entity &entity, const null_t &other) ENTT_NOEXCEPT { + return !(other == entity); +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Compile-time constant for null entities. + * + * There exist implicit conversions from this variable to entity identifiers of + * any allowed type. Similarly, there exist comparision operators between the + * null entity and any other entity identifier. + */ +inline constexpr null_t null{}; + + +} + + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_ENTITY_FWD_HPP +#define ENTT_ENTITY_FWD_HPP + + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +#include +// #include "../config/config.h" + + + +namespace entt { + + +template)> +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; + + +} + + +#endif + + + +namespace entt { + + +template +class basic_sparse_set; + + +template +class basic_storage; + + +template +class basic_registry; + + +template +class basic_view; + + +template +class basic_runtime_view; + + +template +class basic_group; + + +template +class basic_observer; + + +template +class basic_organizer; + + +template +struct basic_handle; + + +template +class basic_snapshot; + + +template +class basic_snapshot_loader; + + +template +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; + + +/** + * @brief Alias declaration for the most common use case. + * @tparam Args Other template parameters. + */ +template +using storage = basic_storage; + + +/*! @brief Alias declaration for the most common use case. */ +using registry = basic_registry; + + +/*! @brief Alias declaration for the most common use case. */ +using observer = basic_observer; + + +/*! @brief Alias declaration for the most common use case. */ +using organizer = basic_organizer; + + +/*! @brief Alias declaration for the most common use case. */ +using handle = basic_handle; + + +/*! @brief Alias declaration for the most common use case. */ +using const_handle = basic_handle; + + +/** + * @brief Alias declaration for the most common use case. + * @tparam Args Other template parameters. + */ +template +using handle_view = basic_handle; + + +/** + * @brief Alias declaration for the most common use case. + * @tparam Args Other template parameters. + */ +template +using const_handle_view = basic_handle; + + +/*! @brief Alias declaration for the most common use case. */ +using snapshot = basic_snapshot; + + +/*! @brief Alias declaration for the most common use case. */ +using snapshot_loader = basic_snapshot_loader; + + +/*! @brief Alias declaration for the most common use case. */ +using continuous_loader = basic_continuous_loader; + + +/** + * @brief Alias declaration for the most common use case. + * @tparam Args Other template parameters. + */ +template +using view = basic_view; + + +/*! @brief Alias declaration for the most common use case. */ +using runtime_view = basic_runtime_view; + + +/** + * @brief Alias declaration for the most common use case. + * @tparam Args Other template parameters. + */ +template +using group = basic_group; + + +} + + +#endif + +// #include "sparse_set.hpp" +#ifndef ENTT_ENTITY_SPARSE_SET_HPP +#define ENTT_ENTITY_SPARSE_SET_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/algorithm.hpp" +#ifndef ENTT_CORE_ALGORITHM_HPP +#define ENTT_CORE_ALGORITHM_HPP + + +#include +#include +#include +#include +#include +// #include "utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + + +#include +// #include "../config/config.h" + + + +namespace entt { + + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type && operator()(Type &&value) const ENTT_NOEXCEPT { + return std::forward(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 +[[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 +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { return func; } + + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) +-> overloaded; /** @@ -1632,7 +5105,7 @@ struct insertion_sort { template> void operator()(It first, It last, Compare compare = Compare{}) const { if(first < last) { - for(auto it = first+1; it < last; ++it) { + for(auto it = first + 1; it < last; ++it) { auto value = std::move(*it); auto pre = it; @@ -1685,17 +5158,17 @@ struct radix_sort { std::size_t index[buckets]{}; std::size_t count[buckets]{}; - std::for_each(from, to, [&getter, &count, start](const value_type &item) { - ++count[(getter(item) >> start) & mask]; - }); + for(auto it = from; it != to; ++it) { + ++count[(getter(*it) >> start) & mask]; + } - std::for_each(std::next(std::begin(index)), std::end(index), [index = std::begin(index), count = std::begin(count)](auto &item) mutable { - item = *(index++) + *(count++); - }); + for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) { + index[pos + 1u] = index[pos] + count[pos]; + } - std::for_each(from, to, [&getter, &out, &index, start](value_type &item) { - out[index[(getter(item) >> start) & mask]++] = std::move(item); - }); + 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) { @@ -1717,78 +5190,7 @@ struct radix_sort { #endif -// #include "../core/fwd.hpp" -#ifndef ENTT_CORE_FWD_HPP -#define ENTT_CORE_FWD_HPP - - -// #include "../config/config.h" - - - -namespace entt { - - -/*! @brief Alias declaration for type identifiers. */ -using id_type = ENTT_ID_TYPE; - - -} - - -#endif - -// #include "../core/type_info.hpp" -#ifndef ENTT_CORE_TYPE_INFO_HPP -#define ENTT_CORE_TYPE_INFO_HPP - - -#include -// #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 "hashed_string.hpp" -#ifndef ENTT_CORE_HASHED_STRING_HPP -#define ENTT_CORE_HASHED_STRING_HPP - - -#include -#include -// #include "../config/config.h" +// #include "entity.hpp" // #include "fwd.hpp" @@ -1798,394 +5200,709 @@ namespace entt { /** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ - - -namespace internal { - - -template -struct fnv1a_traits; - - -template<> -struct fnv1a_traits { - using type = std::uint32_t; - static constexpr std::uint32_t offset = 2166136261; - static constexpr std::uint32_t prime = 16777619; -}; - - -template<> -struct fnv1a_traits { - using type = std::uint64_t; - static constexpr std::uint64_t offset = 14695981039346656037ull; - static constexpr std::uint64_t prime = 1099511628211ull; -}; - - -} - - -/** - * Internal details not to be documented. - * @endcond - */ - - -/** - * @brief Zero overhead unique identifier. + * @brief Basic sparse set implementation. * - * A hashed string is a compile-time tool that allows users to use - * human-readable identifers in the codebase while using their numeric - * counterparts at runtime.
- * Because of that, a hashed string can also be used in constant expressions if - * required. + * Sparse set or packed array or whatever is the name users give it.
+ * 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.
+ * 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. * - * @tparam Char Character type. + * 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). */ -template -class basic_hashed_string { - using traits_type = internal::fnv1a_traits; +template +class basic_sparse_set { + static constexpr auto page_size = ENTT_PAGE_SIZE; - struct const_wrapper { - // non-explicit constructor on purpose - constexpr const_wrapper(const Char *curr) ENTT_NOEXCEPT: str{curr} {} - const Char *str; - }; + using traits_type = entt_traits; + using page_type = std::unique_ptr; - // Fowler–Noll–Vo hash function v. 1a - the good - [[nodiscard]] static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT { - auto value = traits_type::offset; + class sparse_set_iterator final { + friend class basic_sparse_set; - while(*curr != 0) { - value = (value ^ static_cast(*(curr++))) * traits_type::prime; + using packed_type = std::vector; + using index_type = typename traits_type::difference_type; + + sparse_set_iterator(const packed_type &ref, const index_type idx) ENTT_NOEXCEPT + : packed{&ref}, index{idx} + {} + + public: + using difference_type = index_type; + using value_type = Entity; + using pointer = const value_type *; + using reference = const value_type &; + using iterator_category = std::random_access_iterator_tag; + + sparse_set_iterator() ENTT_NOEXCEPT = default; + + sparse_set_iterator & operator++() ENTT_NOEXCEPT { + return --index, *this; } - return value; + sparse_set_iterator operator++(int) ENTT_NOEXCEPT { + iterator orig = *this; + return ++(*this), orig; + } + + sparse_set_iterator & operator--() ENTT_NOEXCEPT { + return ++index, *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 { + index -= 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); + } + + difference_type operator-(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return other.index - index; + } + + [[nodiscard]] reference operator[](const difference_type value) const { + const auto pos = size_type(index-value-1u); + return (*packed)[pos]; + } + + [[nodiscard]] bool operator==(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return other.index == index; + } + + [[nodiscard]] bool operator!=(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + [[nodiscard]] bool operator<(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return index > other.index; + } + + [[nodiscard]] bool operator>(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return index < other.index; + } + + [[nodiscard]] bool operator<=(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return !(*this > other); + } + + [[nodiscard]] bool operator>=(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return !(*this < other); + } + + [[nodiscard]] pointer operator->() const { + const auto pos = size_type(index-1u); + return &(*packed)[pos]; + } + + [[nodiscard]] reference operator*() const { + return *operator->(); + } + + private: + const packed_type *packed; + index_type index; + }; + + [[nodiscard]] auto page(const Entity entt) const ENTT_NOEXCEPT { + return size_type{(to_integral(entt) & traits_type::entity_mask) / page_size}; } + [[nodiscard]] auto offset(const Entity entt) const ENTT_NOEXCEPT { + return size_type{to_integral(entt) & (page_size - 1)}; + } + + [[nodiscard]] page_type & assure(const std::size_t pos) { + if(!(pos < sparse.size())) { + sparse.resize(pos+1); + } + + if(!sparse[pos]) { + sparse[pos].reset(new entity_type[page_size]); + // null is safe in all cases for our purposes + for(auto *first = sparse[pos].get(), *last = first + page_size; first != last; ++first) { + *first = null; + } + } + + return sparse[pos]; + } + +protected: + /*! @brief Swaps two entities in the internal packed array. */ + virtual void swap_at(const std::size_t, const std::size_t) {} + + /*! @brief Attempts to remove an entity from the internal packed array. */ + virtual void swap_and_pop(const std::size_t, void *) {} + public: - /*! @brief Character type. */ - using value_type = Char; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; /*! @brief Unsigned integer type. */ - using hash_type = id_type; + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = sparse_set_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = const entity_type *; + + /*! @brief Default constructor. */ + basic_sparse_set() = default; + + /*! @brief Default move constructor. */ + basic_sparse_set(basic_sparse_set &&) = default; + + /*! @brief Default destructor. */ + virtual ~basic_sparse_set() = default; + + /*! @brief Default move assignment operator. @return This sparse set. */ + basic_sparse_set & operator=(basic_sparse_set &&) = default; /** - * @brief Returns directly the numeric representation of a string. + * @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. + */ + 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]] size_type capacity() const ENTT_NOEXCEPT { + return packed.capacity(); + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + // conservative approach + if(packed.empty()) { + sparse.clear(); + } + + sparse.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() * 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. + * + * 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 internal packed array. + */ + [[nodiscard]] const entity_type * 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 internal packed array. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + const typename traits_type::difference_type pos = packed.size(); + return iterator{packed, pos}; + } + + /** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last entity in + * the internal packed array. Attempting to dereference the returned + * iterator results in undefined behavior. + * + * @return An iterator to the element following the last entity of the + * internal packed array. + */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return iterator{packed, {}}; + } + + /** + * @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]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return packed.data(); + } + + /** + * @brief Returns a reverse iterator to the end. + * + * The returned iterator points to the element following the last entity in + * the reversed internal packed array. Attempting to dereference the + * returned iterator results in undefined behavior. + * + * @return An iterator to the element following the last entity of the + * reversed internal packed array. + */ + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return rbegin() + packed.size(); + } + + /** + * @brief Finds an entity. + * @param entt A valid entity identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const { + return contains(entt) ? --(end() - index(entt)) : end(); + } + + /** + * @brief Checks if a sparse set contains an entity. + * @param entt A valid entity identifier. + * @return True if the sparse set contains the entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const { + const auto curr = page(entt); + // testing against null permits to avoid accessing the packed array + return (curr < sparse.size() && sparse[curr] && sparse[curr][offset(entt)] != null); + } + + /** + * @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 entity identifier. + * @return The position of the entity in the sparse set. + */ + [[nodiscard]] size_type index(const entity_type entt) const { + ENTT_ASSERT(contains(entt)); + return size_type{to_integral(sparse[page(entt)][offset(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 { + 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_ASSERT(pos < packed.size()); + return packed[pos]; + } + + /** + * @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 entity identifier. + */ + void emplace(const entity_type entt) { + ENTT_ASSERT(!contains(entt)); + assure(page(entt))[offset(entt)] = entity_type{static_cast(packed.size())}; + packed.push_back(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. + */ + template + void insert(It first, It last) { + auto next = static_cast(packed.size()); + packed.insert(packed.end(), first, last); + + for(; first != last; ++first) { + ENTT_ASSERT(!contains(*first)); + assure(page(*first))[offset(*first)] = entity_type{next++}; + } + } + + /** + * @brief Removes an entity from a sparse set. + * + * @warning + * Attempting to remove an entity that doesn't belong to the sparse set + * results in undefined behavior. + * + * @param entt A valid entity identifier. + * @param ud Optional user data that are forwarded as-is to derived classes. + */ + void remove(const entity_type entt, void *ud = nullptr) { + ENTT_ASSERT(contains(entt)); + auto &ref = sparse[page(entt)][offset(entt)]; + + // last chance to use the entity for derived classes and mixins, if any + swap_and_pop(size_type{to_integral(ref)}, ud); + + const auto other = packed.back(); + sparse[page(other)][offset(other)] = ref; + // if it looks weird, imagine what the subtle bugs it prevents are + ENTT_ASSERT((packed.back() = entt, true)); + packed[size_type{to_integral(ref)}] = other; + ref = null; + + packed.pop_back(); + } + + /** + * @brief Removes multiple entities from a pool. + * @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 ud Optional user data that are forwarded as-is to derived classes. + */ + template + void remove(It first, It last, void *ud = nullptr) { + for(; first != last; ++first) { + remove(*first, ud); + } + } + + /** + * @copybrief swap_at + * + * 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 entity identifier. + * @param rhs A valid entity identifier. + */ + void swap(const entity_type lhs, const entity_type rhs) { + const auto from = index(lhs); + const auto to = index(rhs); + std::swap(sparse[page(lhs)][offset(lhs)], sparse[page(rhs)][offset(rhs)]); + std::swap(packed[from], packed[to]); + swap_at(from, 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: * - * Forcing template resolution avoids implicit conversions. An - * human-readable identifier can be anything but a plain, old bunch of - * characters.
- * Example of use: * @code{.cpp} - * const auto value = basic_hashed_string::to_value("my.png"); + * bool(const Entity, const Entity); * @endcode * - * @tparam N Number of characters of the identifier. - * @param str Human-readable identifer. - * @return The numeric representation of the string. - */ - template - [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { - return helper(str); - } - - /** - * @brief Returns directly the numeric representation of a string. - * @param wrapper Helps achieving the purpose by relying on overloading. - * @return The numeric representation of the string. - */ - [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { - return helper(wrapper.str); - } - - /** - * @brief Returns directly the numeric representation of a string view. - * @param str Human-readable identifer. - * @param size Length of the string to hash. - * @return The numeric representation of the string. - */ - [[nodiscard]] static hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { - id_type partial{traits_type::offset}; - while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } - return partial; - } - - /*! @brief Constructs an empty hashed string. */ - constexpr basic_hashed_string() ENTT_NOEXCEPT - : str{nullptr}, hash{} - {} - - /** - * @brief Constructs a hashed string from an array of const characters. + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. * - * Forcing template resolution avoids implicit conversions. An - * human-readable identifier can be anything but a plain, old bunch of - * characters.
- * Example of use: - * @code{.cpp} - * basic_hashed_string hs{"my.png"}; - * @endcode + * The sort function object must offer a member function template + * `operator()` that accepts three arguments: * - * @tparam N Number of characters of the identifier. - * @param curr Human-readable identifer. + * * 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 count Number of elements to sort. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. */ - template - constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT - : str{curr}, hash{helper(curr)} - {} + template + void sort_n(const size_type count, Compare compare, Sort algo = Sort{}, Args &&... args) { + ENTT_ASSERT(!(count > size())); - /** - * @brief Explicit constructor on purpose to avoid constructing a hashed - * string directly from a `const value_type *`. - * @param wrapper Helps achieving the purpose by relying on overloading. - */ - explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT - : str{wrapper.str}, hash{helper(wrapper.str)} - {} + algo(packed.rend() - count, packed.rend(), std::move(compare), std::forward(args)...); - /** - * @brief Returns the human-readable representation of a hashed string. - * @return The string used to initialize the instance. - */ - [[nodiscard]] constexpr const value_type * data() const ENTT_NOEXCEPT { - return str; + for(size_type pos{}; pos < count; ++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); + sparse[page(entt)][offset(entt)] = entity_type{static_cast(curr)}; + + curr = next; + next = idx; + } + } } /** - * @brief Returns the numeric representation of a hashed string. - * @return The numeric representation of the instance. + * @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. */ - [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { - return hash; + template + void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { + sort_n(size(), std::move(compare), std::move(algo), std::forward(args)...); } - /*! @copydoc data */ - [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { return data(); } + /** + * @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.
+ * 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) { + const auto to = other.end(); + auto from = other.begin(); + + size_type pos = packed.size() - 1; + + while(pos && from != to) { + if(contains(*from)) { + if(*from != packed[pos]) { + swap(packed[pos], *from); + } + + --pos; + } + + ++from; + } + } /** - * @brief Returns the numeric representation of a hashed string. - * @return The numeric representation of the instance. + * @brief Clears a sparse set. + * @param ud Optional user data that are forwarded as-is to derived classes. */ - [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } - - /** - * @brief Compares two hashed strings. - * @param other Hashed string with which to compare. - * @return True if the two hashed strings are identical, false otherwise. - */ - [[nodiscard]] constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { - return hash == other.hash; + void clear(void *ud = nullptr) ENTT_NOEXCEPT { + remove(begin(), end(), ud); } private: - const value_type *str; - hash_type hash; + std::vector sparse; + std::vector packed; }; -/** - * @brief Deduction guide. - * - * It allows to deduce the character type of the hashed string directly from a - * human-readable identifer provided to the constructor. - * - * @tparam Char Character type. - * @tparam N Number of characters of the identifier. - * @param str Human-readable identifer. - */ -template -basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT --> basic_hashed_string; - - -/** - * @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 -[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - - -/*! @brief Aliases for common character types. */ -using hashed_string = basic_hashed_string; - - -/*! @brief Aliases for common character types. */ -using hashed_wstring = basic_hashed_string; - - -} - - -/** - * @brief User defined literal for hashed strings. - * @param str The literal without its suffix. - * @return A properly initialized hashed string. - */ -[[nodiscard]] constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT { - return entt::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 entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { - return entt::hashed_wstring{str}; } #endif -// #include "fwd.hpp" +// #include "storage.hpp" +#ifndef ENTT_ENTITY_STORAGE_HPP +#define ENTT_ENTITY_STORAGE_HPP +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" -namespace entt { +// #include "../core/algorithm.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../signal/sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H -namespace internal { +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif -struct ENTT_API type_index { - [[nodiscard]] static id_type next() ENTT_NOEXCEPT { - static ENTT_MAYBE_ATOMIC(id_type) value{}; - return value++; - } -}; - - -template -[[nodiscard]] constexpr auto 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; +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type #else - return std::string_view{}; +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif -} -} +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif -/** - * Internal details not to be documented. - * @endcond - */ - - -/** - * @brief Type index. - * @tparam Type Type for which to generate a sequential identifier. - */ -template -struct ENTT_API type_index { - /** - * @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; - } -}; - - -/** - * @brief Provides the member constant `value` to true if a given type is - * indexable, false otherwise. - * @tparam Type Potentially indexable type. - */ -template -struct has_type_index: std::false_type {}; - - -/*! @brief has_type_index */ -template -struct has_type_index::value())>>: std::true_type {}; - - -/** - * @brief Helper variable template. - * @tparam Type Potentially indexable type. - */ -template -inline constexpr bool has_type_index_v = has_type_index::value; - - -/** - * @brief Type info. - * @tparam Type Type for which to generate information. - */ -template -struct type_info { - /** - * @brief Returns the numeric representation of a given type. - * @return The numeric representation of the given type. - */ -#if defined ENTT_PRETTY_FUNCTION_CONSTEXPR - [[nodiscard]] static constexpr id_type id() ENTT_NOEXCEPT { - constexpr auto value = hashed_string::value(ENTT_PRETTY_FUNCTION); - return value; - } -#elif defined ENTT_PRETTY_FUNCTION - [[nodiscard]] static id_type id() ENTT_NOEXCEPT { - static const auto value = hashed_string::value(ENTT_PRETTY_FUNCTION); - return value; - } +#ifdef ENTT_PAGE_SIZE +static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); #else - [[nodiscard]] static id_type id() ENTT_NOEXCEPT { - return type_index::value(); - } +# define ENTT_PAGE_SIZE 4096 #endif - /** - * @brief Returns the name of a given type. - * @return The name of the given type. - */ -#if defined ENTT_PRETTY_FUNCTION_CONSTEXPR - [[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT { - constexpr auto value = internal::type_name(); - return value; - } -#elif defined ENTT_PRETTY_FUNCTION - [[nodiscard]] static std::string_view name() ENTT_NOEXCEPT { - static const auto value = internal::type_name(); - return value; - } + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty #else - [[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT { - return internal::type_name(); - } +# include +# define ENTT_IS_EMPTY(Type) std::false_type #endif -}; -} +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif #endif +// #include "delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + + +#include +#include +#include +#include +#include // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP @@ -2195,14 +5912,177 @@ struct type_info { #include #include // #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifdef ENTT_PAGE_SIZE +static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); +#else +# define ENTT_PAGE_SIZE 4096 +#endif + + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#endif // #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +#include +// #include "../config/config.h" namespace entt { +template)> +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; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // Unfortunately, doxygen cannot parse such a construct. + /*! @cond TURN_OFF_DOXYGEN */ + : choice_t + /*! @endcond */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t 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 +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::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 +struct size_of: std::integral_constant {}; + + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr auto size_of_v = size_of::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. @@ -2230,7 +6110,7 @@ using integral_constant = std::integral_constant; /** - * @brief Alias template to ease the creation of named values. + * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template @@ -2238,57 +6118,64 @@ using tag = integral_constant; /** - * @brief Utility class to disambiguate overloaded functions. - * @tparam N Number of choices available. - */ -template -struct choice_t - // Unfortunately, doxygen cannot parse such a construct. - /*! @cond TURN_OFF_DOXYGEN */ - : choice_t - /*! @endcond */ -{}; - - -/*! @copybrief choice_t */ -template<> -struct choice_t<0> {}; - - -/** - * @brief Variable template for the choice trick. - * @tparam N Number of choices available. - */ -template -inline constexpr choice_t choice{}; - - -/*! @brief A class to use to push around lists of types, nothing more. */ -template -struct type_list {}; - - -/*! @brief Primary template isn't defined on purpose. */ -template -struct type_list_size; - - -/** - * @brief Compile-time number of elements in a type list. + * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template -struct type_list_size> - : std::integral_constant +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 +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 +struct type_list_element> + : type_list_element> {}; /** - * @brief Helper variable template. - * @tparam List Type list. + * @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 -inline constexpr auto type_list_size_v = type_list_size::value; +template +struct type_list_element<0u, type_list> { + /*! @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 +using type_list_element_t = typename type_list_element::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 +constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ @@ -2373,20 +6260,213 @@ template using type_list_unique_t = typename type_list_unique::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 +struct type_list_contains; + + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other>: std::disjunction...> {}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr auto type_list_contains_v = type_list_contains::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +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 +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 +struct value_list_element> + : value_list_element> +{}; + + +/** + * @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 +struct value_list_element<0u, value_list> { + /*! @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 +inline constexpr auto value_list_element_v = value_list_element::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 +constexpr value_list operator+(value_list, value_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +[[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) +-> decltype(std::declval() == std::declval()) { return true; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>); +} + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type Potentially equality comparable type. */ -template> -struct is_equality_comparable: std::false_type {}; - - -/*! @copydoc is_equality_comparable */ -template -struct is_equality_comparable() == std::declval())>> - : std::true_type -{}; +template +struct is_equality_comparable: std::bool_constant(choice<2>)> {}; /** @@ -2397,15 +6477,96 @@ template inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; +/*! @brief Same as std::is_invocable, but with tuples. */ +template +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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** +* @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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr auto is_applicable_v = is_applicable::value; + + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +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 +struct is_applicable_r>: std::is_invocable_r {}; + + +/** + * @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 +inline constexpr auto is_applicable_r_v = is_applicable_r::value; + + +/** +* @brief Provides the member constant `value` to true if a given type is +* complete, false otherwise. +* @tparam Type Potential complete type. +*/ +template +struct is_complete: std::false_type {}; + + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + + +/** +* @brief Helper variable template. +* @tparam Type Potential complete type. +*/ +template +inline constexpr auto is_complete_v = is_complete::value; + + /** * @brief Provides the member constant `value` to true if a given type is empty * and the empty type optimization is enabled, false otherwise. * @tparam Type Potential empty type. */ template -struct is_eto_eligible - : ENTT_IS_EMPTY(Type) -{}; +struct is_empty: ENTT_IS_EMPTY(Type) {}; /** @@ -2413,7 +6574,36 @@ struct is_eto_eligible * @tparam Type Potential empty type. */ template -inline constexpr auto is_eto_eligible_v = is_eto_eligible::value; +inline constexpr auto is_empty_v = is_empty::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 +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::add_const_t; +}; + + +/** + * @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 +using constness_as_t = typename constness_as::type; /** @@ -2452,109 +6642,6 @@ using member_class_t = typename member_class::type; #endif -// #include "../signal/sigh.hpp" -#ifndef ENTT_SIGNAL_SIGH_HPP -#define ENTT_SIGNAL_SIGH_HPP - - -#include -#include -#include -#include -#include -#include -// #include "../config/config.h" -#ifndef ENTT_CONFIG_CONFIG_H -#define ENTT_CONFIG_CONFIG_H - - -#ifndef ENTT_NOEXCEPT -# define ENTT_NOEXCEPT noexcept -#endif - - -#ifndef ENTT_HS_SUFFIX -# define ENTT_HS_SUFFIX _hs -#endif - - -#ifndef ENTT_HWS_SUFFIX -# define ENTT_HWS_SUFFIX _hws -#endif - - -#ifndef ENTT_USE_ATOMIC -# define ENTT_MAYBE_ATOMIC(Type) Type -#else -# include -# define ENTT_MAYBE_ATOMIC(Type) std::atomic -#endif - - -#ifndef ENTT_ID_TYPE -# include -# define ENTT_ID_TYPE std::uint32_t -#endif - - -#ifndef ENTT_PAGE_SIZE -# define ENTT_PAGE_SIZE 32768 -#endif - - -#ifndef ENTT_ASSERT -# include -# define ENTT_ASSERT(condition) assert(condition) -#endif - - -#ifndef ENTT_NO_ETO -# include -# define ENTT_IS_EMPTY(Type) std::is_empty -#else -# include -# define ENTT_IS_EMPTY(Type) std::false_type -#endif - - -#ifndef ENTT_STANDARD_CPP -# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) -# define ENTT_PRETTY_FUNCTION_CONSTEXPR -# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ -# define ENTT_PRETTY_FUNCTION_PREFIX '=' -# define ENTT_PRETTY_FUNCTION_SUFFIX ']' -# elif 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_CONSTEXPR -# define ENTT_PRETTY_FUNCTION __FUNCSIG__ -# define ENTT_PRETTY_FUNCTION_PREFIX '<' -# define ENTT_PRETTY_FUNCTION_SUFFIX '>' -# endif -#endif - - -#ifndef ENTT_STANDALONE -# define ENTT_FAST_PATH(...) false -#else -# define ENTT_FAST_PATH(Cond) Cond -#endif - - -#endif - -// #include "delegate.hpp" -#ifndef ENTT_SIGNAL_DELEGATE_HPP -#define ENTT_SIGNAL_DELEGATE_HPP - - -#include -#include -#include -#include -#include // #include "../config/config.h" @@ -2648,7 +6735,7 @@ class delegate { [[nodiscard]] auto wrap(std::index_sequence) ENTT_NOEXCEPT { return [](const void *, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); - return Ret(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); }; } @@ -2656,8 +6743,8 @@ class delegate { [[nodiscard]] auto wrap(Type &, std::index_sequence) ENTT_NOEXCEPT { return [](const void *payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); - Type *curr = static_cast(const_cast, const void *, void *>>(payload)); - return Ret(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + Type *curr = static_cast(const_cast *>(payload)); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); }; } @@ -2665,8 +6752,8 @@ class delegate { [[nodiscard]] auto wrap(Type *, std::index_sequence) ENTT_NOEXCEPT { return [](const void *payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); - Type *curr = static_cast(const_cast, const void *, void *>>(payload)); - return Ret(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + Type *curr = static_cast(const_cast *>(payload)); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); }; } @@ -2728,7 +6815,7 @@ public: return Ret(std::invoke(Candidate, std::forward(args)...)); }; } else if constexpr(std::is_member_pointer_v) { - fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); + fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); } else { fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); } @@ -2755,7 +6842,7 @@ public: if constexpr(std::is_invocable_r_v) { fn = [](const void *payload, Args... args) -> Ret { - Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + Type *curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); }; } else { @@ -2779,7 +6866,7 @@ public: if constexpr(std::is_invocable_r_v) { fn = [](const void *payload, Args... args) -> Ret { - Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + Type *curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, curr, std::forward(args)...)); }; } else { @@ -2830,15 +6917,13 @@ public: * * @warning * Attempting to trigger an invalid delegate results in undefined - * behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * delegate has not yet been set. + * 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(fn); + ENTT_ASSERT(static_cast(*this)); return fn(data, std::forward(args)...); } @@ -2885,7 +6970,7 @@ template * @tparam Candidate Function or member to connect to the delegate. */ template -delegate(connect_arg_t) ENTT_NOEXCEPT +delegate(connect_arg_t) -> delegate>>; @@ -2895,13 +6980,17 @@ delegate(connect_arg_t) ENTT_NOEXCEPT * @tparam Type Type of class or type of payload. */ template -delegate(connect_arg_t, Type &&) ENTT_NOEXCEPT +delegate(connect_arg_t, Type &&) -> delegate>>; -/*! @brief Deduction guide. */ +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ template -delegate(Ret(*)(const void *, Args...), const void * = nullptr) ENTT_NOEXCEPT +delegate(Ret(*)(const void *, Args...), const void * = nullptr) -> delegate; @@ -3194,7 +7283,7 @@ private: * 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. @@ -3447,7 +7536,8 @@ private: * @tparam Args Types of arguments of a function type. */ template -sink(sigh &) ENTT_NOEXCEPT -> sink; +sink(sigh &) +-> sink; } @@ -3455,1005 +7545,11 @@ sink(sigh &) ENTT_NOEXCEPT -> sink; #endif -// #include "entity.hpp" -#ifndef ENTT_ENTITY_ENTITY_HPP -#define ENTT_ENTITY_ENTITY_HPP - - -#include -#include -#include -// #include "../config/config.h" - - - -namespace entt { - - -/** - * @brief Entity traits. - * - * Primary template isn't defined on purpose. All the specializations give a - * compile-time error unless the template parameter is an accepted entity type. - */ -template -struct entt_traits; - - -/** - * @brief Entity traits for enumeration types. - * @tparam Type The type to check. - */ -template -struct entt_traits>> - : entt_traits> -{}; - - -/** - * @brief Entity traits for a 16 bits entity identifier. - * - * A 16 bits entity identifier guarantees: - * - * * 12 bits for the entity number (up to 4k entities). - * * 4 bit for the version (resets in [0-15]). - */ -template<> -struct entt_traits { - /*! @brief Underlying entity type. */ - using entity_type = std::uint16_t; - /*! @brief Underlying version type. */ - using version_type = std::uint8_t; - /*! @brief Difference type. */ - using difference_type = std::int16_t; - - /*! @brief Mask to use to get the entity number out of an identifier. */ - static constexpr entity_type entity_mask = 0xFFF; - /*! @brief Mask to use to get the version out of an identifier. */ - static constexpr entity_type version_mask = 0xF; - /*! @brief Extent of the entity number within an identifier. */ - static constexpr std::size_t entity_shift = 12u; -}; - - -/** - * @brief Entity traits for a 32 bits entity identifier. - * - * A 32 bits entity identifier guarantees: - * - * * 20 bits for the entity number (suitable for almost all the games). - * * 12 bit for the version (resets in [0-4095]). - */ -template<> -struct entt_traits { - /*! @brief Underlying entity type. */ - using entity_type = std::uint32_t; - /*! @brief Underlying version type. */ - using version_type = std::uint16_t; - /*! @brief Difference type. */ - using difference_type = std::int32_t; - - /*! @brief Mask to use to get the entity number out of an identifier. */ - static constexpr entity_type entity_mask = 0xFFFFF; - /*! @brief Mask to use to get the version out of an identifier. */ - static constexpr entity_type version_mask = 0xFFF; - /*! @brief Extent of the entity number within an identifier. */ - static constexpr std::size_t entity_shift = 20u; -}; - - -/** - * @brief Entity traits for a 64 bits entity identifier. - * - * A 64 bits entity identifier guarantees: - * - * * 32 bits for the entity number (an indecently large number). - * * 32 bit for the version (an indecently large number). - */ -template<> -struct entt_traits { - /*! @brief Underlying entity type. */ - using entity_type = std::uint64_t; - /*! @brief Underlying version type. */ - using version_type = std::uint32_t; - /*! @brief Difference type. */ - using difference_type = std::int64_t; - - /*! @brief Mask to use to get the entity number out of an identifier. */ - static constexpr entity_type entity_mask = 0xFFFFFFFF; - /*! @brief Mask to use to get the version out of an identifier. */ - static constexpr entity_type version_mask = 0xFFFFFFFF; - /*! @brief Extent of the entity number within an identifier. */ - static constexpr std::size_t entity_shift = 32u; -}; - - -/** - * @brief Converts an entity type to its underlying type. - * @tparam Entity The value type. - * @param entity The value to convert. - * @return The integral representation of the given value. - */ -template -[[nodiscard]] constexpr auto to_integral(const Entity entity) ENTT_NOEXCEPT { - return static_cast::entity_type>(entity); -} - - -/*! @brief Null object for all entity identifiers. */ -struct null_t { - /** - * @brief Converts the null object to identifiers of any type. - * @tparam Entity Type of entity identifier. - * @return The null representation for the given identifier. - */ - template - [[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT { - return Entity{entt_traits::entity_mask}; - } - - /** - * @brief Compares two null objects. - * @return True in all cases. - */ - [[nodiscard]] constexpr bool operator==(null_t) const ENTT_NOEXCEPT { - return true; - } - - /** - * @brief Compares two null objects. - * @return False in all cases. - */ - [[nodiscard]] constexpr bool operator!=(null_t) const ENTT_NOEXCEPT { - return false; - } - - /** - * @brief Compares a null object and an entity identifier of any type. - * @tparam Entity Type of entity identifier. - * @param entity Entity identifier with which to compare. - * @return False if the two elements differ, true otherwise. - */ - template - [[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT { - return (to_integral(entity) & entt_traits::entity_mask) == to_integral(static_cast(*this)); - } - - /** - * @brief Compares a null object and an entity identifier of any type. - * @tparam Entity Type of entity identifier. - * @param entity Entity identifier with which to compare. - * @return True if the two elements differ, false otherwise. - */ - template - [[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT { - return !(entity == *this); - } -}; - - -/** - * @brief Compares a null object and an entity identifier of any type. - * @tparam Entity Type of entity identifier. - * @param entity Entity identifier with which to compare. - * @param other A null object yet to be converted. - * @return False if the two elements differ, true otherwise. - */ -template -[[nodiscard]] constexpr bool operator==(const Entity entity, null_t other) ENTT_NOEXCEPT { - return other.operator==(entity); -} - - -/** - * @brief Compares a null object and an entity identifier of any type. - * @tparam Entity Type of entity identifier. - * @param entity Entity identifier with which to compare. - * @param other A null object yet to be converted. - * @return True if the two elements differ, false otherwise. - */ -template -[[nodiscard]] constexpr bool operator!=(const Entity entity, null_t other) ENTT_NOEXCEPT { - return !(other == entity); -} - - -/** - * Internal details not to be documented. - * @endcond - */ - - -/** - * @brief Compile-time constant for null entities. - * - * There exist implicit conversions from this variable to entity identifiers of - * any allowed type. Similarly, there exist comparision operators between the - * null entity and any other entity identifier. - */ -inline constexpr null_t null{}; - - -} - - -#endif - -// #include "fwd.hpp" -#ifndef ENTT_ENTITY_FWD_HPP -#define ENTT_ENTITY_FWD_HPP - - -// #include "../core/fwd.hpp" - - - -namespace entt { - - -template -class basic_registry; - - -template -class basic_view; - - -template -class basic_runtime_view; - - -template -class basic_group; - - -template -class basic_observer; - - -template -struct basic_actor; - - -template -struct basic_handle; - - -template -class basic_snapshot; - - -template -class basic_snapshot_loader; - - -template -class basic_continuous_loader; - - -/*! @brief Default entity identifier. */ -enum class entity: id_type {}; - - -/*! @brief Alias declaration for the most common use case. */ -using registry = basic_registry; - - -/*! @brief Alias declaration for the most common use case. */ -using observer = basic_observer; - - -/*! @brief Alias declaration for the most common use case. */ -using actor [[deprecated("Consider using the handle class instead")]] = basic_actor; - - -/*! @brief Alias declaration for the most common use case. */ -using handle = basic_handle; - - -/*! @brief Alias declaration for the most common use case. */ -using const_handle = basic_handle; - - -/*! @brief Alias declaration for the most common use case. */ -using snapshot = basic_snapshot; - - -/*! @brief Alias declaration for the most common use case. */ -using snapshot_loader = basic_snapshot_loader; - - -/*! @brief Alias declaration for the most common use case. */ -using continuous_loader = basic_continuous_loader; - - -/** - * @brief Alias declaration for the most common use case. - * @tparam Types Types of components iterated by the view. - */ -template -using view = basic_view; - - -/*! @brief Alias declaration for the most common use case. */ -using runtime_view = basic_runtime_view; - - -/** - * @brief Alias declaration for the most common use case. - * @tparam Types Types of components iterated by the group. - */ -template -using group = basic_group; - - -} - - -#endif - -// #include "group.hpp" -#ifndef ENTT_ENTITY_GROUP_HPP -#define ENTT_ENTITY_GROUP_HPP - - -#include -#include -#include -// #include "../config/config.h" - -// #include "../core/type_traits.hpp" - // #include "entity.hpp" // #include "fwd.hpp" -// #include "pool.hpp" -#ifndef ENTT_ENTITY_POOL_HPP -#define ENTT_ENTITY_POOL_HPP - - -#include -// #include "storage.hpp" -#ifndef ENTT_ENTITY_STORAGE_HPP -#define ENTT_ENTITY_STORAGE_HPP - - -#include -#include -#include -#include -#include -#include -// #include "../config/config.h" - -// #include "../core/algorithm.hpp" - -// #include "../core/type_traits.hpp" - -// #include "entity.hpp" - // #include "sparse_set.hpp" -#ifndef ENTT_ENTITY_SPARSE_SET_HPP -#define ENTT_ENTITY_SPARSE_SET_HPP - - -#include -#include -#include -#include -#include -#include -// #include "../config/config.h" - -// #include "../core/algorithm.hpp" - -// #include "entity.hpp" - -// #include "fwd.hpp" - - - -namespace entt { - - -/** - * @brief Basic sparse set implementation. - * - * Sparse set or packed array or whatever is the name users give it.
- * 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.
- * 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). - */ -template -class sparse_set { - static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); - static constexpr auto entt_per_page = ENTT_PAGE_SIZE / sizeof(Entity); - - using traits_type = entt_traits; - using page_type = std::unique_ptr; - - class sparse_set_iterator final { - friend class sparse_set; - - using packed_type = std::vector; - using index_type = typename traits_type::difference_type; - - sparse_set_iterator(const packed_type &ref, const index_type idx) ENTT_NOEXCEPT - : packed{&ref}, index{idx} - {} - - public: - using difference_type = index_type; - using value_type = Entity; - using pointer = const value_type *; - using reference = const value_type &; - using iterator_category = std::random_access_iterator_tag; - - sparse_set_iterator() ENTT_NOEXCEPT = default; - - sparse_set_iterator & operator++() ENTT_NOEXCEPT { - return --index, *this; - } - - sparse_set_iterator operator++(int) ENTT_NOEXCEPT { - iterator orig = *this; - return ++(*this), orig; - } - - sparse_set_iterator & operator--() ENTT_NOEXCEPT { - return ++index, *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 { - index -= 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); - } - - difference_type operator-(const sparse_set_iterator &other) const ENTT_NOEXCEPT { - return other.index - index; - } - - [[nodiscard]] reference operator[](const difference_type value) const { - const auto pos = size_type(index-value-1u); - return (*packed)[pos]; - } - - [[nodiscard]] bool operator==(const sparse_set_iterator &other) const ENTT_NOEXCEPT { - return other.index == index; - } - - [[nodiscard]] bool operator!=(const sparse_set_iterator &other) const ENTT_NOEXCEPT { - return !(*this == other); - } - - [[nodiscard]] bool operator<(const sparse_set_iterator &other) const ENTT_NOEXCEPT { - return index > other.index; - } - - [[nodiscard]] bool operator>(const sparse_set_iterator &other) const ENTT_NOEXCEPT { - return index < other.index; - } - - [[nodiscard]] bool operator<=(const sparse_set_iterator &other) const ENTT_NOEXCEPT { - return !(*this > other); - } - - [[nodiscard]] bool operator>=(const sparse_set_iterator &other) const ENTT_NOEXCEPT { - return !(*this < other); - } - - [[nodiscard]] pointer operator->() const { - const auto pos = size_type(index-1u); - return &(*packed)[pos]; - } - - [[nodiscard]] reference operator*() const { - return *operator->(); - } - - private: - const packed_type *packed; - index_type index; - }; - - [[nodiscard]] auto page(const Entity entt) const ENTT_NOEXCEPT { - return size_type{(to_integral(entt) & traits_type::entity_mask) / entt_per_page}; - } - - [[nodiscard]] auto offset(const Entity entt) const ENTT_NOEXCEPT { - return size_type{to_integral(entt) & (entt_per_page - 1)}; - } - - [[nodiscard]] page_type & assure(const std::size_t pos) { - if(!(pos < sparse.size())) { - sparse.resize(pos+1); - } - - if(!sparse[pos]) { - sparse[pos].reset(new entity_type[entt_per_page]); - // null is safe in all cases for our purposes - for(auto *first = sparse[pos].get(), *last = first + entt_per_page; first != last; ++first) { - *first = null; - } - } - - return sparse[pos]; - } - -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 = sparse_set_iterator; - /*! @brief Reverse iterator type. */ - using reverse_iterator = const entity_type *; - - /*! @brief Default constructor. */ - sparse_set() = default; - - /*! @brief Default move constructor. */ - sparse_set(sparse_set &&) = default; - - /*! @brief Default destructor. */ - virtual ~sparse_set() = default; - - /*! @brief Default move assignment operator. @return This sparse set. */ - sparse_set & operator=(sparse_set &&) = default; - - /** - * @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. - */ - 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]] size_type capacity() const ENTT_NOEXCEPT { - return packed.capacity(); - } - - /*! @brief Requests the removal of unused capacity. */ - void shrink_to_fit() { - // conservative approach - if(packed.empty()) { - sparse.clear(); - } - - sparse.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() * entt_per_page; - } - - /** - * @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. - * - * 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 internal packed array. - */ - [[nodiscard]] const entity_type * 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 internal packed array. - */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { - const typename traits_type::difference_type pos = packed.size(); - return iterator{packed, pos}; - } - - /** - * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last entity in - * the internal packed array. Attempting to dereference the returned - * iterator results in undefined behavior. - * - * @return An iterator to the element following the last entity of the - * internal packed array. - */ - [[nodiscard]] iterator end() const ENTT_NOEXCEPT { - return iterator{packed, {}}; - } - - /** - * @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]] reverse_iterator rbegin() const ENTT_NOEXCEPT { - return packed.data(); - } - - /** - * @brief Returns a reverse iterator to the end. - * - * The returned iterator points to the element following the last entity in - * the reversed internal packed array. Attempting to dereference the - * returned iterator results in undefined behavior. - * - * @return An iterator to the element following the last entity of the - * reversed internal packed array. - */ - [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { - return rbegin() + packed.size(); - } - - /** - * @brief Finds an entity. - * @param entt A valid entity identifier. - * @return An iterator to the given entity if it's found, past the end - * iterator otherwise. - */ - [[nodiscard]] iterator find(const entity_type entt) const { - return contains(entt) ? --(end() - index(entt)) : end(); - } - - /** - * @brief Checks if a sparse set contains an entity. - * @param entt A valid entity identifier. - * @return True if the sparse set contains the entity, false otherwise. - */ - [[nodiscard]] bool contains(const entity_type entt) const { - const auto curr = page(entt); - // testing against null permits to avoid accessing the packed array - return (curr < sparse.size() && sparse[curr] && sparse[curr][offset(entt)] != null); - } - - /** - * @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.
- * An assertion will abort the execution at runtime in debug mode if the - * sparse set doesn't contain the given entity. - * - * @param entt A valid entity identifier. - * @return The position of the entity in the sparse set. - */ - [[nodiscard]] size_type index(const entity_type entt) const { - ENTT_ASSERT(contains(entt)); - return size_type{to_integral(sparse[page(entt)][offset(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.
- * An assertion will abort the execution at runtime in debug mode if the - * sparse set already contains the given entity. - * - * @param entt A valid entity identifier. - */ - void emplace(const entity_type entt) { - ENTT_ASSERT(!contains(entt)); - assure(page(entt))[offset(entt)] = entity_type(static_cast(packed.size())); - packed.push_back(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.
- * An assertion will abort the execution at runtime in debug mode if the - * sparse set already contains the given entity. - * - * @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 - void insert(It first, It last) { - auto next = static_cast(packed.size()); - packed.insert(packed.end(), first, last); - - while(first != last) { - const auto entt = *(first++); - ENTT_ASSERT(!contains(entt)); - assure(page(entt))[offset(entt)] = entity_type(next++); - } - } - - /** - * @brief Removes an entity from a sparse set. - * - * @warning - * Attempting to remove an entity that doesn't belong to the sparse set - * results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * sparse set doesn't contain the given entity. - * - * @param entt A valid entity identifier. - */ - void erase(const entity_type entt) { - ENTT_ASSERT(contains(entt)); - const auto curr = page(entt); - const auto pos = offset(entt); - packed[size_type{to_integral(sparse[curr][pos])}] = packed.back(); - sparse[page(packed.back())][offset(packed.back())] = sparse[curr][pos]; - sparse[curr][pos] = null; - packed.pop_back(); - } - - /** - * @brief Swaps two entities in the internal packed array. - * - * 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.
- * An assertion will abort the execution at runtime in debug mode if the - * sparse set doesn't contain the given entities. - * - * @param lhs A valid entity identifier. - * @param rhs A valid entity identifier. - */ - virtual void swap(const entity_type lhs, const entity_type rhs) { - auto &from = sparse[page(lhs)][offset(lhs)]; - auto &to = sparse[page(rhs)][offset(rhs)]; - std::swap(packed[size_type{to_integral(from)}], packed[size_type{to_integral(to)}]); - std::swap(from, to); - } - - /** - * @brief Sort elements according to the given comparison function. - * - * Sort the elements so that iterating the range with a couple of iterators - * returns them 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 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 oject must offer a member function template - * `operator()` that accepts three arguments: - * - * * An iterator to the first element of the range to sort. - * * An iterator past the last element of the range to sort. - * * A comparison function to use to compare the elements. - * - * @tparam 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 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 algo A valid sort function object. - * @param args Arguments to forward to the sort function object, if any. - */ - template - void sort(iterator first, iterator last, Compare compare, Sort algo = Sort{}, Args &&... args) { - ENTT_ASSERT(!(last < first)); - ENTT_ASSERT(!(last > end())); - - const auto length = std::distance(first, last); - const auto skip = std::distance(last, end()); - const auto to = packed.rend() - skip; - const auto from = to - length; - - algo(from, to, std::move(compare), std::forward(args)...); - - for(size_type pos = skip, end = skip+length; pos < end; ++pos) { - sparse[page(packed[pos])][offset(packed[pos])] = entity_type(static_cast(pos)); - } - } - - /** - * @brief Sort elements according to the given comparison function. - * - * @sa sort - * - * This function is a slightly slower version of `sort` that invokes the - * caller to indicate which entities are swapped.
- * It's recommended when the caller wants to sort its own data structures to - * align them with the order induced in the sparse set. - * - * The signature of the callback should be equivalent to the following: - * - * @code{.cpp} - * bool(const Entity, const Entity); - * @endcode - * - * @tparam Apply Type of function object to invoke to notify the caller. - * @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 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 apply A valid function object to use as a callback. - * @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 - void arrange(iterator first, iterator last, Apply apply, Compare compare, Sort algo = Sort{}, Args &&... args) { - ENTT_ASSERT(!(last < first)); - ENTT_ASSERT(!(last > end())); - - const auto length = std::distance(first, last); - const auto skip = std::distance(last, end()); - const auto to = packed.rend() - skip; - const auto from = to - length; - - algo(from, to, std::move(compare), std::forward(args)...); - - for(size_type pos = skip, end = skip+length; pos < end; ++pos) { - auto curr = pos; - auto next = index(packed[curr]); - - while(curr != next) { - apply(packed[curr], packed[next]); - sparse[page(packed[curr])][offset(packed[curr])] = entity_type(static_cast(curr)); - - curr = next; - next = index(packed[curr]); - } - } - } - - /** - * @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.
- * 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 sparse_set &other) { - const auto to = other.end(); - auto from = other.begin(); - - size_type pos = packed.size() - 1; - - while(pos && from != to) { - if(contains(*from)) { - if(*from != packed[pos]) { - swap(packed[pos], *from); - } - - --pos; - } - - ++from; - } - } - - /** - * @brief Clears a sparse set. - */ - void clear() ENTT_NOEXCEPT { - sparse.clear(); - packed.clear(); - } - -private: - std::vector sparse; - std::vector packed; -}; - - -} - - -#endif @@ -4487,18 +7583,18 @@ namespace entt { * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Type Type of objects assigned to the entities. */ -template> -class storage: public sparse_set { +template +class basic_storage: public basic_sparse_set { static_assert(std::is_move_constructible_v && std::is_move_assignable_v, "The managed type must be at least move constructible and assignable"); - using underlying_type = sparse_set; + using underlying_type = basic_sparse_set; using traits_type = entt_traits; - template + template class storage_iterator final { - friend class storage; + friend class basic_storage; - using instance_type = std::conditional_t, std::vector>; + using instance_type = constness_as_t, Value>; using index_type = typename traits_type::difference_type; storage_iterator(instance_type &ref, const index_type idx) ENTT_NOEXCEPT @@ -4507,9 +7603,9 @@ class storage: public sparse_set { public: using difference_type = index_type; - using value_type = Type; - using pointer = std::conditional_t; - using reference = std::conditional_t; + using value_type = Value; + using pointer = value_type *; + using reference = value_type &; using iterator_category = std::random_access_iterator_tag; storage_iterator() ENTT_NOEXCEPT = default; @@ -4597,23 +7693,42 @@ class storage: public sparse_set { index_type index; }; +protected: + /** + * @copybrief basic_sparse_set::swap_at + * @param lhs A valid position of an entity within storage. + * @param rhs A valid position of an entity within storage. + */ + void swap_at(const std::size_t lhs, const std::size_t rhs) { + std::swap(instances[lhs], instances[rhs]); + } + + /** + * @copybrief basic_sparse_set::swap_and_pop + * @param pos A valid position of an entity within storage. + */ + void swap_and_pop(const std::size_t pos, void *) { + auto other = std::move(instances.back()); + instances[pos] = std::move(other); + instances.pop_back(); + } + public: - /*! @brief Type of the objects associated with the entities. */ - using object_type = Type; + /*! @brief Type of the objects assigned to entities. */ + using value_type = Type; /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Random access iterator type. */ - using iterator = storage_iterator; + using iterator = storage_iterator; /*! @brief Constant random access iterator type. */ - using const_iterator = storage_iterator; + using const_iterator = storage_iterator; /*! @brief Reverse iterator type. */ using reverse_iterator = Type *; /*! @brief Constant reverse iterator type. */ using const_reverse_iterator = const Type *; - /** * @brief Increases the capacity of a storage. * @@ -4636,7 +7751,7 @@ public: /** * @brief Direct access to the array of objects. * - * The returned pointer is such that range `[raw(), raw() + size()]` is + * The returned pointer is such that range `[raw(), raw() + size())` is * always a valid range, even if the container is empty. * * @note @@ -4645,13 +7760,13 @@ public: * * @return A pointer to the array of objects. */ - [[nodiscard]] const object_type * raw() const ENTT_NOEXCEPT { + [[nodiscard]] const value_type * raw() const ENTT_NOEXCEPT { return instances.data(); } /*! @copydoc raw */ - [[nodiscard]] object_type * raw() ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).raw()); + [[nodiscard]] value_type * raw() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).raw()); } /** @@ -4750,38 +7865,22 @@ public: } /** - * @brief Returns the object associated with an entity. + * @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.
- * An assertion will abort the execution at runtime in debug mode if the - * storage doesn't contain the given entity. + * undefined behavior. * * @param entt A valid entity identifier. - * @return The object associated with the entity. + * @return The object assigned to the entity. */ - [[nodiscard]] const object_type & get(const entity_type entt) const { + [[nodiscard]] const value_type & get(const entity_type entt) const { return instances[underlying_type::index(entt)]; } /*! @copydoc get */ - [[nodiscard]] object_type & get(const entity_type entt) { - return const_cast(std::as_const(*this).get(entt)); - } - - /** - * @brief Returns a pointer to the object associated with an entity, if any. - * @param entt A valid entity identifier. - * @return The object associated with the entity, if any. - */ - [[nodiscard]] const object_type * try_get(const entity_type entt) const { - return underlying_type::contains(entt) ? (instances.data() + underlying_type::index(entt)) : nullptr; - } - - /*! @copydoc try_get */ - [[nodiscard]] object_type * try_get(const entity_type entt) { - return const_cast(std::as_const(*this).try_get(entt)); + [[nodiscard]] value_type & get(const entity_type entt) { + return const_cast(std::as_const(*this).get(entt)); } /** @@ -4793,17 +7892,16 @@ public: * * @warning * Attempting to use an entity that already belongs to the storage results - * in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * storage already contains the given entity. + * in undefined behavior. * * @tparam Args Types of arguments to use to construct the object. * @param entt A valid entity identifier. * @param args Parameters to use to construct an object for the entity. + * @return A reference to the newly created object. */ template - void emplace(const entity_type entt, Args &&... args) { - if constexpr(std::is_aggregate_v) { + value_type & emplace(const entity_type entt, Args &&... args) { + if constexpr(std::is_aggregate_v) { instances.push_back(Type{std::forward(args)...}); } else { instances.emplace_back(std::forward(args)...); @@ -4811,6 +7909,21 @@ public: // entity goes after component in case constructor throws underlying_type::emplace(entt); + return instances.back(); + } + + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entity A valid entity identifier. + * @param func Valid function objects. + * @return A reference to the updated instance. + */ + template + decltype(auto) patch(const entity_type entity, Func &&... func) { + auto &&instance = instances[this->index(entity)]; + (std::forward(func)(instance), ...); + return instance; } /** @@ -4819,9 +7932,7 @@ public: * * @warning * Attempting to assign an entity that already belongs to the storage - * results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * storage already contains the given entity. + * results in undefined behavior. * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. @@ -4829,7 +7940,7 @@ public: * @param value An instance of the object to construct. */ template - void insert(It first, It last, const object_type &value = {}) { + void insert(It first, It last, const value_type &value = {}) { instances.insert(instances.end(), std::distance(first, last), value); // entities go after components in case constructors throw underlying_type::insert(first, last); @@ -4855,48 +7966,9 @@ public: underlying_type::insert(first, last); } - /** - * @brief Removes an entity from a storage and destroys its object. - * - * @warning - * Attempting to use an entity that doesn't belong to the storage results in - * undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * storage doesn't contain the given entity. - * - * @param entt A valid entity identifier. - */ - void erase(const entity_type entt) { - auto other = std::move(instances.back()); - instances[underlying_type::index(entt)] = std::move(other); - instances.pop_back(); - underlying_type::erase(entt); - } - - /** - * @brief Swaps entities and objects in the internal packed arrays. - * - * @warning - * Attempting to swap entities that don't belong to the sparse set results - * in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * sparse set doesn't contain the given entities. - * - * @param lhs A valid entity identifier. - * @param rhs A valid entity identifier. - */ - void swap(const entity_type lhs, const entity_type rhs) override { - std::swap(instances[underlying_type::index(lhs)], instances[underlying_type::index(rhs)]); - underlying_type::swap(lhs, rhs); - } - /** * @brief Sort elements according to the given comparison function. * - * Sort the elements so that iterating the range with a couple of iterators - * returns them 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: @@ -4924,65 +7996,76 @@ public: * @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 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 count Number of elements to sort. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template - void sort(iterator first, iterator last, Compare compare, Sort algo = Sort{}, Args &&... args) { - ENTT_ASSERT(!(last < first)); - ENTT_ASSERT(!(last > end())); - - const auto from = underlying_type::begin() + std::distance(begin(), first); - const auto to = from + std::distance(first, last); - - const auto apply = [this](const auto lhs, const auto rhs) { - std::swap(instances[underlying_type::index(lhs)], instances[underlying_type::index(rhs)]); - }; - - if constexpr(std::is_invocable_v) { - underlying_type::arrange(from, to, std::move(apply), [this, compare = std::move(compare)](const auto lhs, const auto rhs) { + void sort_n(const size_type count, Compare compare, Sort algo = Sort{}, Args &&... args) { + if constexpr(std::is_invocable_v) { + underlying_type::sort_n(count, [this, compare = std::move(compare)](const auto lhs, const auto rhs) { return compare(std::as_const(instances[underlying_type::index(lhs)]), std::as_const(instances[underlying_type::index(rhs)])); }, std::move(algo), std::forward(args)...); } else { - underlying_type::arrange(from, to, std::move(apply), std::move(compare), std::move(algo), std::forward(args)...); + underlying_type::sort_n(count, std::move(compare), std::move(algo), std::forward(args)...); } } - /*! @brief Clears a storage. */ - void clear() { - underlying_type::clear(); - instances.clear(); + /** + * @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 + void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { + sort_n(this->size(), std::move(compare), std::move(algo), std::forward(args)...); } private: - std::vector instances; + std::vector instances; }; -/*! @copydoc storage */ +/*! @copydoc basic_storage */ template -class storage>>: public sparse_set { - using underlying_type = sparse_set; +class basic_storage>>: public basic_sparse_set { + using underlying_type = basic_sparse_set; public: - /*! @brief Type of the objects associated with the entities. */ - using object_type = Type; + /*! @brief Type of the objects assigned to entities. */ + using value_type = Type; /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; + /** + * @brief Fake get function. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid entity identifier. + */ + void get([[maybe_unused]] const entity_type entt) const { + ENTT_ASSERT(this->contains(entt)); + } + /** * @brief Assigns an entity to a storage and constructs its object. * * @warning * Attempting to use an entity that already belongs to the storage results - * in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * storage already contains the given entity. + * in undefined behavior. * * @tparam Args Types of arguments to use to construct the object. * @param entt A valid entity identifier. @@ -4990,43 +8073,245 @@ public: */ template void emplace(const entity_type entt, Args &&... args) { - [[maybe_unused]] object_type instance{std::forward(args)...}; + [[maybe_unused]] value_type instance{std::forward(args)...}; underlying_type::emplace(entt); } + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entity A valid entity identifier. + * @param func Valid function objects. + */ + template + void patch([[maybe_unused]] const entity_type entity, Func &&... func) { + ENTT_ASSERT(this->contains(entity)); + (std::forward(func)(), ...); + } + /** * @brief Assigns one or more entities to a storage. * * @warning * Attempting to assign an entity that already belongs to the storage - * results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * storage already contains the given entity. + * results in undefined behavior. * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template - void insert(It first, It last, const object_type & = {}) { + void insert(It first, It last, const value_type & = {}) { underlying_type::insert(first, last); } }; -} +/** + * @brief Mixin type to use to wrap basic storage classes. + * @tparam Type The type of the underlying storage. + */ +template +struct storage_adapter_mixin: Type { + static_assert(std::is_same_v>, "Invalid object type"); + /*! @brief Type of the objects assigned to entities. */ + using value_type = typename Type::value_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename Type::entity_type; -#endif + /** + * @brief Assigns entities to a storage. + * @tparam Args Types of arguments to use to construct the object. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the object. + * @return A reference to the newly created object. + */ + template + decltype(auto) emplace(basic_registry &, const entity_type entity, Args &&... args) { + return Type::emplace(entity, std::forward(args)...); + } + /** + * @brief Assigns entities to a storage. + * @tparam It Type of input iterator. + * @tparam Args Types of arguments to use to construct the objects assigned + * to the entities. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param args Parameters to use to initialize the objects assigned to the + * entities. + */ + template + void insert(basic_registry &, It first, It last, Args &&... args) { + Type::insert(first, last, std::forward(args)...); + } - -namespace entt { + /** + * @brief Patches the given instance for an entity. + * @tparam Func Types of the function objects to invoke. + * @param entity A valid entity identifier. + * @param func Valid function objects. + * @return A reference to the patched instance. + */ + template + decltype(auto) patch(basic_registry &, const entity_type entity, Func &&... func) { + return Type::patch(entity, std::forward(func)...); + } +}; /** - * @brief Applies component-to-pool conversion and defines the resulting type as - * the member typedef type. + * @brief Mixin type to use to add signal support to storage types. + * @tparam Type The type of the underlying storage. + */ +template +class sigh_storage_mixin final: public Type { + /** + * @copybrief basic_sparse_set::swap_and_pop + * @param pos A valid position of an entity within storage. + * @param ud Optional user data that are forwarded as-is to derived classes. + */ + void swap_and_pop(const std::size_t pos, void *ud) final { + ENTT_ASSERT(ud != nullptr); + const auto entity = basic_sparse_set::operator[](pos); + destruction.publish(*static_cast *>(ud), entity); + // the position may have changed due to the actions of a listener + Type::swap_and_pop(this->index(entity), ud); + } + +public: + /*! @brief Underlying value type. */ + using value_type = typename Type::value_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever a new instance is created and assigned to an entity.
+ * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, entity_type); + * @endcode + * + * Listeners are invoked **after** the object has been assigned to the + * entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_construct() ENTT_NOEXCEPT { + return sink{construction}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is explicitly updated.
+ * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, entity_type); + * @endcode + * + * Listeners are invoked **after** the object has been updated. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_update() ENTT_NOEXCEPT { + return sink{update}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is removed from an entity and thus destroyed.
+ * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, entity_type); + * @endcode + * + * Listeners are invoked **before** the object has been removed from the + * entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_destroy() ENTT_NOEXCEPT { + return sink{destruction}; + } + + /** + * @brief Assigns entities to a storage. + * @tparam Args Types of arguments to use to construct the object. + * @param owner The registry that issued the request. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the object. + * @return A reference to the newly created object. + */ + template + decltype(auto) emplace(basic_registry &owner, const entity_type entity, Args &&... args) { + Type::emplace(entity, std::forward(args)...); + construction.publish(owner, entity); + return this->get(entity); + } + + /** + * @brief Assigns entities to a storage. + * @tparam It Type of input iterator. + * @tparam Args Types of arguments to use to construct the objects assigned + * to the entities. + * @param owner The registry that issued the request. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param args Parameters to use to initialize the objects assigned to the + * entities. + */ + template + void insert(basic_registry &owner, It first, It last, Args &&... args) { + Type::insert(first, last, std::forward(args)...); + + if(!construction.empty()) { + for(; first != last; ++first) { + construction.publish(owner, *first); + } + } + } + + /** + * @brief Patches the given instance for an entity. + * @tparam Func Types of the function objects to invoke. + * @param owner The registry that issued the request. + * @param entity A valid entity identifier. + * @param func Valid function objects. + * @return A reference to the patched instance. + */ + template + decltype(auto) patch(basic_registry &owner, const entity_type entity, Func &&... func) { + Type::patch(entity, std::forward(func)...); + update.publish(owner, entity); + return this->get(entity); + } + +private: + sigh &, const entity_type)> construction{}; + sigh &, const entity_type)> destruction{}; + sigh &, const entity_type)> update{}; +}; + + +/** + * @brief Defines the component-to-storage conversion. * * Formally: * @@ -5039,27 +8324,29 @@ namespace entt { * @tparam Type Type of objects assigned to the entities. */ template -struct pool { - /*! @brief Resulting type after component-to-pool conversion. */ - using type = storage; -}; - - -/*! @copydoc pool */ -template -struct pool { - /*! @brief Resulting type after component-to-pool conversion. */ - using type = std::add_const_t>::type>; +struct storage_traits { + /*! @brief Resulting type after component-to-storage conversion. */ + using storage_type = sigh_storage_mixin>; }; /** - * @brief Alias declaration to use to make component-to-pool conversions. - * @tparam Entity A valid entity type (see entt_traits for more details). - * @tparam Type Type of objects assigned to the entities. + * @brief Gets the element assigned to an entity from a storage, if any. + * @tparam Type Storage type. + * @param container A valid instance of a storage class. + * @param entity A valid entity identifier. + * @return A possibly empty tuple containing the requested element. */ -template -using pool_t = typename pool::type; +template +[[nodiscard]] auto get_as_tuple([[maybe_unused]] Type &container, [[maybe_unused]] const typename Type::entity_type entity) { + static_assert(std::is_same_v, typename storage_traits::storage_type>, "Invalid storage"); + + if constexpr(std::is_void_v) { + return std::make_tuple(); + } else { + return std::forward_as_tuple(container.get(entity)); + } +} } @@ -5067,8 +8354,6 @@ using pool_t = typename pool::type; #endif -// #include "sparse_set.hpp" - // #include "utility.hpp" #ifndef ENTT_ENTITY_UTILITY_HPP #define ENTT_ENTITY_UTILITY_HPP @@ -5170,141 +8455,116 @@ class basic_group; * @tparam Get Type of components observed by the group. */ template -class basic_group, get_t> { +class basic_group, get_t> final { /*! @brief A registry is allowed to create groups. */ friend class basic_registry; template - using pool_type = pool_t; + using storage_type = constness_as_t>::storage_type, Component>; - class group_proxy { + class iterable_group final { friend class basic_group, get_t>; - class proxy_iterator { - friend class group_proxy; + template + class iterable_group_iterator final { + friend class iterable_group; - using it_type = typename sparse_set::iterator; - using ref_type = decltype(std::tuple_cat(std::declval, std::tuple<>, std::tuple *>>>()...)); - - proxy_iterator(it_type from, ref_type ref) ENTT_NOEXCEPT + template + iterable_group_iterator(It from, const std::tuple *...> &args) ENTT_NOEXCEPT : it{from}, - pools{ref} + pools{args} {} public: using difference_type = std::ptrdiff_t; - using value_type = decltype(std::tuple_cat( - std::declval>(), - std::declval, std::tuple<>, std::tuple>>()... - )); + using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); using pointer = void; - using reference = decltype(std::tuple_cat( - std::declval>(), - std::declval, std::tuple<>, std::tuple>>()... - )); + using reference = value_type; using iterator_category = std::input_iterator_tag; - proxy_iterator & operator++() ENTT_NOEXCEPT { + iterable_group_iterator & operator++() ENTT_NOEXCEPT { return ++it, *this; } - proxy_iterator operator++(int) ENTT_NOEXCEPT { - proxy_iterator orig = *this; + iterable_group_iterator operator++(int) ENTT_NOEXCEPT { + iterable_group_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { - return std::apply([entt = *it](auto *... cpool) { return reference{entt, cpool->get(entt)...}; }, pools); + const auto entt = *it; + return std::tuple_cat(std::make_tuple(entt), get_as_tuple(*std::get *>(pools), entt)...); } - [[nodiscard]] bool operator==(const proxy_iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator==(const iterable_group_iterator &other) const ENTT_NOEXCEPT { return other.it == it; } - [[nodiscard]] bool operator!=(const proxy_iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator!=(const iterable_group_iterator &other) const ENTT_NOEXCEPT { return !(*this == other); } private: - it_type it{}; - ref_type pools{}; + It it; + std::tuple *...> pools; }; - group_proxy(const sparse_set &ref, std::tuple *...> gpools) - : handler{&ref}, - pools{gpools} + iterable_group(basic_sparse_set * const ref, const std::tuple *...> &cpools) + : handler{ref}, + pools{cpools} {} public: - using iterator = proxy_iterator; + using iterator = iterable_group_iterator::iterator>; + using reverse_iterator = iterable_group_iterator::reverse_iterator>; [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { - return proxy_iterator{handler->begin(), std::tuple_cat([](auto *cpool) { - if constexpr(is_eto_eligible_v::object_type>) { - return std::make_tuple(); - } else { - return std::make_tuple(cpool); - } - }(std::get *>(pools))...)}; + return handler ? iterator{handler->begin(), pools} : iterator{{}, pools}; } [[nodiscard]] iterator end() const ENTT_NOEXCEPT { - return proxy_iterator{handler->end(), std::tuple_cat([](auto *cpool) { - if constexpr(is_eto_eligible_v::object_type>) { - return std::make_tuple(); - } else { - return std::make_tuple(cpool); - } - }(std::get *>(pools))...)}; + return handler ? iterator{handler->end(), pools} : iterator{{}, pools}; + } + + [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return handler ? reverse_iterator{handler->rbegin(), pools} : reverse_iterator{{}, pools}; + } + + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return handler ? reverse_iterator{handler->rend(), pools} : reverse_iterator{{}, pools}; } private: - const sparse_set *handler; - std::tuple *...> pools; + basic_sparse_set * const handler; + const std::tuple *...> pools; }; - basic_group(sparse_set &ref, pool_type &... gpool) ENTT_NOEXCEPT + basic_group(basic_sparse_set &ref, storage_type &... gpool) ENTT_NOEXCEPT : handler{&ref}, pools{&gpool...} {} - template - void traverse(Func func, type_list) const { - for(const auto entt: *handler) { - if constexpr(std::is_invocable_v({}))...>) { - func(std::get *>(pools)->get(entt)...); - } else { - func(entt, std::get *>(pools)->get(entt)...); - } - } - } - 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 sparse_set::iterator; + using iterator = typename basic_sparse_set::iterator; /*! @brief Reversed iterator type. */ - using reverse_iterator = typename sparse_set::reverse_iterator; + using reverse_iterator = typename basic_sparse_set::reverse_iterator; - /** - * @brief Returns the number of existing components of the given type. - * @tparam Component Type of component of which to return the size. - * @return Number of existing components of the given type. - */ - template - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { - return std::get *>(pools)->size(); - } + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() ENTT_NOEXCEPT + : handler{} + {} /** * @brief Returns the number of entities that have the given components. * @return Number of entities that have the given components. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { - return handler->size(); + return *this ? handler->size() : size_type{}; } /** @@ -5313,80 +8573,34 @@ public: * @return Capacity of the group. */ [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { - return handler->capacity(); + return *this ? handler->capacity() : size_type{}; } /*! @brief Requests the removal of unused capacity. */ void shrink_to_fit() { - handler->shrink_to_fit(); - } - - /** - * @brief Checks whether a group or some pools are empty. - * @tparam Component Types of components in which one is interested. - * @return True if the group or the pools are empty, false otherwise. - */ - template - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { - if constexpr(sizeof...(Component) == 0) { - return handler->empty(); - } else { - return (std::get *>(pools)->empty() && ...); + if(*this) { + handler->shrink_to_fit(); } } /** - * @brief Direct access to the list of components of a given pool. - * - * The returned pointer is such that range - * `[raw(), raw() + size()]` is always a - * valid range, even if the container is empty. - * - * @note - * Components are in the reverse order as returned by the `begin`/`end` - * iterators. - * - * @tparam Component Type of component in which one is interested. - * @return A pointer to the array of components. + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. */ - template - [[nodiscard]] Component * raw() const ENTT_NOEXCEPT { - return std::get *>(pools)->raw(); - } - - /** - * @brief Direct access to the list of entities of a given pool. - * - * 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. - * - * @tparam Component Type of component in which one is interested. - * @return A pointer to the array of entities. - */ - template - [[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT { - return std::get *>(pools)->data(); + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return !*this || handler->empty(); } /** * @brief Direct access to the list of entities. * - * The returned pointer is such that range `[data(), data() + size()]` is + * 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 handler->data(); + return *this ? handler->data() : nullptr; } /** @@ -5395,14 +8609,10 @@ public: * The returned iterator points to the first entity of the group. If the * group is empty, the returned iterator will be equal to `end()`. * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the first entity of the group. */ [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { - return handler->begin(); + return *this ? handler->begin() : iterator{}; } /** @@ -5412,15 +8622,11 @@ public: * the group. Attempting to dereference the returned iterator results in * undefined behavior. * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the entity following the last entity of the * group. */ [[nodiscard]] iterator end() const ENTT_NOEXCEPT { - return handler->end(); + return *this ? handler->end() : iterator{}; } /** @@ -5429,14 +8635,10 @@ public: * 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()`. * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the first entity of the reversed group. */ [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { - return handler->rbegin(); + return *this ? handler->rbegin() : reverse_iterator{}; } /** @@ -5447,15 +8649,11 @@ public: * the reversed group. Attempting to dereference the returned iterator * results in undefined behavior. * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the entity following the last entity of the * reversed group. */ [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { - return handler->rend(); + return *this ? handler->rend() : reverse_iterator{}; } /** @@ -5485,7 +8683,7 @@ public: * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const { - const auto it = handler->find(entt); + const auto it = *this ? handler->find(entt) : iterator{}; return it != end() && *it == entt ? it : end(); } @@ -5498,13 +8696,21 @@ public: return begin()[pos]; } + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return handler != nullptr; + } + /** * @brief Checks if a group contains an entity. * @param entt A valid entity identifier. * @return True if the group contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const { - return handler->contains(entt); + return *this && handler->contains(entt); } /** @@ -5516,22 +8722,22 @@ public: * @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.
- * An assertion will abort the execution at runtime in debug mode if the - * group doesn't contain the given entity. + * results in undefined behavior. * * @tparam Component Types of components to get. * @param entt A valid entity identifier. * @return The components assigned to the entity. */ template - [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const { + [[nodiscard]] decltype(auto) get(const entity_type entt) const { ENTT_ASSERT(contains(entt)); - if constexpr(sizeof...(Component) == 1) { - return (std::get *>(pools)->get(entt), ...); + if constexpr(sizeof...(Component) == 0) { + return std::tuple_cat(get_as_tuple(*std::get *>(pools), entt)...); + } else if constexpr(sizeof...(Component) == 1) { + return (std::get *>(pools)->get(entt), ...); } else { - return std::tuple({}))...>{get(entt)...}; + return std::tuple_cat(get_as_tuple(*std::get *>(pools), entt)...); } } @@ -5559,8 +8765,13 @@ public: */ template void each(Func func) const { - using get_type_list = type_list_cat_t, type_list<>, type_list>...>; - traverse(std::move(func), get_type_list{}); + for(const auto entt: *this) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); + } else { + std::apply(func, get(entt)); + } + } } /** @@ -5576,8 +8787,8 @@ public: * * @return An iterable object to use to _visit_ the group. */ - [[nodiscard]] auto proxy() const ENTT_NOEXCEPT { - return group_proxy{*handler, pools}; + [[nodiscard]] iterable_group each() const ENTT_NOEXCEPT { + return iterable_group{handler, pools}; } /** @@ -5618,17 +8829,19 @@ public: */ template void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { - if constexpr(sizeof...(Component) == 0) { - static_assert(std::is_invocable_v, "Invalid comparison function"); - handler->sort(handler->begin(), handler->end(), std::move(compare), std::move(algo), std::forward(args)...); - } else if constexpr(sizeof...(Component) == 1) { - handler->sort(handler->begin(), handler->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { - return compare((std::get *>(pools)->get(lhs), ...), (std::get *>(pools)->get(rhs), ...)); - }, std::move(algo), std::forward(args)...); - } else { - handler->sort(handler->begin(), handler->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { - return compare(std::tuple({}))...>{std::get *>(pools)->get(lhs)...}, std::tuple({}))...>{std::get *>(pools)->get(rhs)...}); - }, std::move(algo), std::forward(args)...); + if(*this) { + if constexpr(sizeof...(Component) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + handler->sort(std::move(compare), std::move(algo), std::forward(args)...); + } else if constexpr(sizeof...(Component) == 1) { + handler->sort([this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { + return compare((std::get *>(pools)->get(lhs), ...), (std::get *>(pools)->get(rhs), ...)); + }, std::move(algo), std::forward(args)...); + } else { + handler->sort([this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { + return compare(std::forward_as_tuple(std::get *>(pools)->get(lhs)...), std::forward_as_tuple(std::get *>(pools)->get(rhs)...)); + }, std::move(algo), std::forward(args)...); + } } } @@ -5650,12 +8863,14 @@ public: */ template void sort() const { - handler->respect(*std::get *>(pools)); + if(*this) { + handler->respect(*std::get *>(pools)); + } } private: - sparse_set *handler; - const std::tuple *...> pools; + basic_sparse_set * const handler; + const std::tuple *...> pools; }; @@ -5706,256 +8921,186 @@ private: * @tparam Owned Types of components owned by the group. */ template -class basic_group, get_t, Owned...> { +class basic_group, get_t, Owned...> final { /*! @brief A registry is allowed to create groups. */ friend class basic_registry; template - using pool_type = pool_t; + using storage_type = constness_as_t>::storage_type, Component>; - template - using component_iterator = decltype(std::declval>().begin()); - - class group_proxy { + class iterable_group final { friend class basic_group, get_t, Owned...>; - class proxy_iterator { - friend class group_proxy; + template + class iterable_group_iterator; - using it_type = typename sparse_set::iterator; - using owned_type = decltype(std::tuple_cat(std::declval, std::tuple<>, std::tuple>>>()...)); - using get_type = decltype(std::tuple_cat(std::declval, std::tuple<>, std::tuple *>>>()...)); + template + class iterable_group_iterator> final { + friend class iterable_group; - proxy_iterator(it_type from, owned_type oref, get_type gref) ENTT_NOEXCEPT + template + iterable_group_iterator(It from, const std::tuple &other, const std::tuple *...> &cpools) ENTT_NOEXCEPT : it{from}, - owned{oref}, - get{gref} + owned{std::get(other)...}, + get{cpools} {} public: using difference_type = std::ptrdiff_t; - using value_type = decltype(std::tuple_cat( - std::declval>(), - std::declval, std::tuple<>, std::tuple>>()..., - std::declval, std::tuple<>, std::tuple>>()... - )); + using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); using pointer = void; - using reference = decltype(std::tuple_cat( - std::declval>(), - std::declval, std::tuple<>, std::tuple>>()..., - std::declval, std::tuple<>, std::tuple>>()... - )); + using reference = value_type; using iterator_category = std::input_iterator_tag; - proxy_iterator & operator++() ENTT_NOEXCEPT { - return ++it, std::apply([](auto &&... curr) { (++curr, ...); }, owned), *this; + iterable_group_iterator & operator++() ENTT_NOEXCEPT { + return ++it, (++std::get(owned), ...), *this; } - proxy_iterator operator++(int) ENTT_NOEXCEPT { - proxy_iterator orig = *this; + iterable_group_iterator operator++(int) ENTT_NOEXCEPT { + iterable_group_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { return std::tuple_cat( std::make_tuple(*it), - std::apply([](auto &&... curr) { return std::forward_as_tuple(*curr...); }, owned), - std::apply([entt = *it](auto &&... curr) { return std::forward_as_tuple(curr->get(entt)...); }, get) + std::forward_as_tuple(*std::get(owned)...), + get_as_tuple(*std::get *>(get), *it)... ); } - [[nodiscard]] bool operator==(const proxy_iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator==(const iterable_group_iterator &other) const ENTT_NOEXCEPT { return other.it == it; } - [[nodiscard]] bool operator!=(const proxy_iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator!=(const iterable_group_iterator &other) const ENTT_NOEXCEPT { return !(*this == other); } private: - it_type it{}; - owned_type owned{}; - get_type get{}; + It it; + std::tuple owned; + std::tuple *...> get; }; - group_proxy(std::tuple *..., pool_type *...> cpools, const std::size_t &extent) + iterable_group(std::tuple *..., storage_type *...> cpools, const std::size_t * const extent) : pools{cpools}, - length{&extent} + length{extent} {} public: - using iterator = proxy_iterator; + using iterator = iterable_group_iterator< + typename basic_sparse_set::iterator, + type_list_cat_t>().get({}))>, type_list<>, type_list>().end())>>...> + >; + using reverse_iterator = iterable_group_iterator< + typename basic_sparse_set::reverse_iterator, + type_list_cat_t>().get({}))>, type_list<>, type_list>().rbegin())>>...> + >; [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { - return proxy_iterator{ - std::get<0>(pools)->sparse_set::end() - *length, - std::tuple_cat([length = *length](auto *cpool) { - if constexpr(is_eto_eligible_v::object_type>) { - return std::make_tuple(); - } else { - return std::make_tuple(cpool->end() - length); - } - }(std::get *>(pools))...), - std::tuple_cat([](auto *cpool) { - if constexpr(is_eto_eligible_v::object_type>) { - return std::make_tuple(); - } else { - return std::make_tuple(cpool); - } - }(std::get *>(pools))...) - }; + return length ? iterator{ + std::get<0>(pools)->basic_sparse_set::end() - *length, + std::make_tuple((std::get *>(pools)->end() - *length)...), + std::make_tuple(std::get *>(pools)...) + } : iterator{{}, std::make_tuple(decltype(std::get *>(pools)->end()){}...), std::make_tuple(std::get *>(pools)...)}; } [[nodiscard]] iterator end() const ENTT_NOEXCEPT { - return proxy_iterator{ - std::get<0>(pools)->sparse_set::end(), - std::tuple_cat([](auto *cpool) { - if constexpr(is_eto_eligible_v::object_type>) { - return std::make_tuple(); - } else { - return std::make_tuple(cpool->end()); - } - }(std::get *>(pools))...), - std::tuple_cat([](auto *cpool) { - if constexpr(is_eto_eligible_v::object_type>) { - return std::make_tuple(); - } else { - return std::make_tuple(cpool); - } - }(std::get *>(pools))...) - }; + return length ? iterator{ + std::get<0>(pools)->basic_sparse_set::end(), + std::make_tuple((std::get *>(pools)->end())...), + std::make_tuple(std::get *>(pools)...) + } : iterator{{}, std::make_tuple(decltype(std::get *>(pools)->end()){}...), std::make_tuple(std::get *>(pools)...)}; + } + + [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return length ? reverse_iterator{ + std::get<0>(pools)->basic_sparse_set::rbegin(), + std::make_tuple((std::get *>(pools)->rbegin())...), + std::make_tuple(std::get *>(pools)...) + } : reverse_iterator{{}, std::make_tuple(decltype(std::get *>(pools)->rbegin()){}...), std::make_tuple(std::get *>(pools)...)}; + } + + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return length ? reverse_iterator{ + std::get<0>(pools)->basic_sparse_set::rbegin() + *length, + std::make_tuple((std::get *>(pools)->rbegin() + *length)...), + std::make_tuple(std::get *>(pools)...) + } : reverse_iterator{{}, std::make_tuple(decltype(std::get *>(pools)->rbegin()){}...), std::make_tuple(std::get *>(pools)...)}; } private: - const std::tuple *..., pool_type *...> pools; - const std::size_t *length; + const std::tuple *..., storage_type *...> pools; + const std::size_t * const length; }; - basic_group(const std::size_t &extent, pool_type &... opool, pool_type &... gpool) ENTT_NOEXCEPT + basic_group(const std::size_t &extent, storage_type &... opool, storage_type &... gpool) ENTT_NOEXCEPT : pools{&opool..., &gpool...}, length{&extent} {} - template - void traverse(Func func, type_list, type_list) const { - [[maybe_unused]] auto it = std::make_tuple((std::get *>(pools)->end() - *length)...); - [[maybe_unused]] auto data = std::get<0>(pools)->sparse_set::end() - *length; - - for(auto next = *length; next; --next) { - if constexpr(std::is_invocable_v({}))..., decltype(get({}))...>) { - if constexpr(sizeof...(Weak) == 0) { - func(*(std::get>(it)++)...); - } else { - const auto entt = *(data++); - func(*(std::get>(it)++)..., std::get *>(pools)->get(entt)...); - } - } else { - const auto entt = *(data++); - func(entt, *(std::get>(it)++)..., std::get *>(pools)->get(entt)...); - } - } - } - 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 sparse_set::iterator; + using iterator = typename basic_sparse_set::iterator; /*! @brief Reversed iterator type. */ - using reverse_iterator = typename sparse_set::reverse_iterator; + using reverse_iterator = typename basic_sparse_set::reverse_iterator; - /** - * @brief Returns the number of existing components of the given type. - * @tparam Component Type of component of which to return the size. - * @return Number of existing components of the given type. - */ - template - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { - return std::get *>(pools)->size(); - } + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() ENTT_NOEXCEPT + : length{} + {} /** * @brief Returns the number of entities that have the given components. * @return Number of entities that have the given components. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { - return *length; + return *this ? *length : size_type{}; } /** - * @brief Checks whether a group or some pools are empty. - * @tparam Component Types of components in which one is interested. - * @return True if the group or the pools are empty, false otherwise. + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. */ - template [[nodiscard]] bool empty() const ENTT_NOEXCEPT { - if constexpr(sizeof...(Component) == 0) { - return !*length; - } else { - return (std::get *>(pools)->empty() && ...); - } + return !*this || !*length; } /** * @brief Direct access to the list of components of a given pool. * * The returned pointer is such that range - * `[raw(), raw() + size()]` is always a - * valid range, even if the container is empty.
- * Moreover, in case the group owns the given component, the range - * `[raw(), raw() + size()]` is such that it contains - * the instances that are part of the group itself. + * `[raw(), raw() + size())` is always a valid range, + * even if the container is empty.
* - * @note - * Components are in the reverse order as returned by the `begin`/`end` - * iterators. + * @warning + * This function is only available for owned types. * * @tparam Component Type of component in which one is interested. * @return A pointer to the array of components. */ template [[nodiscard]] Component * raw() const ENTT_NOEXCEPT { - return std::get *>(pools)->raw(); - } - - /** - * @brief Direct access to the list of entities of a given pool. - * - * The returned pointer is such that range - * `[data(), data() + size()]` is always a - * valid range, even if the container is empty.
- * Moreover, in case the group owns the given component, the range - * `[data(), data() + size()]` is such that it - * contains the entities that are part of the group itself. - * - * @note - * Entities are in the reverse order as returned by the `begin`/`end` - * iterators. - * - * @tparam Component Type of component in which one is interested. - * @return A pointer to the array of entities. - */ - template - [[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT { - return std::get *>(pools)->data(); + static_assert((std::is_same_v || ...), "Non-owned type"); + auto *cpool = std::get *>(pools); + return cpool ? cpool->raw() : nullptr; } /** * @brief Direct access to the list of entities. * - * The returned pointer is such that range `[data(), data() + size()]` is + * 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 std::get<0>(pools)->data(); + return *this ? std::get<0>(pools)->data() : nullptr; } /** @@ -5964,14 +9109,10 @@ public: * The returned iterator points to the first entity of the group. If the * group is empty, the returned iterator will be equal to `end()`. * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the first entity of the group. */ [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { - return std::get<0>(pools)->sparse_set::end() - *length; + return *this ? (std::get<0>(pools)->basic_sparse_set::end() - *length) : iterator{}; } /** @@ -5981,15 +9122,11 @@ public: * the group. Attempting to dereference the returned iterator results in * undefined behavior. * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the entity following the last entity of the * group. */ [[nodiscard]] iterator end() const ENTT_NOEXCEPT { - return std::get<0>(pools)->sparse_set::end(); + return *this ? std::get<0>(pools)->basic_sparse_set::end() : iterator{}; } /** @@ -5998,14 +9135,10 @@ public: * 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()`. * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the first entity of the reversed group. */ [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { - return std::get<0>(pools)->sparse_set::rbegin(); + return *this ? std::get<0>(pools)->basic_sparse_set::rbegin() : reverse_iterator{}; } /** @@ -6016,15 +9149,11 @@ public: * the reversed group. Attempting to dereference the returned iterator * results in undefined behavior. * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the entity following the last entity of the * reversed group. */ [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { - return std::get<0>(pools)->sparse_set::rbegin() + *length; + return *this ? (std::get<0>(pools)->basic_sparse_set::rbegin() + *length) : reverse_iterator{}; } /** @@ -6054,7 +9183,7 @@ public: * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const { - const auto it = std::get<0>(pools)->find(entt); + const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{}; return it != end() && it >= begin() && *it == entt ? it : end(); } @@ -6067,13 +9196,21 @@ public: return begin()[pos]; } + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return length != nullptr; + } + /** * @brief Checks if a group contains an entity. * @param entt A valid entity identifier. * @return True if the group contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const { - return std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length)); + return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length)); } /** @@ -6085,22 +9222,22 @@ public: * @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.
- * An assertion will abort the execution at runtime in debug mode if the - * group doesn't contain the given entity. + * results in undefined behavior. * * @tparam Component Types of components to get. * @param entt A valid entity identifier. * @return The components assigned to the entity. */ template - [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const { + [[nodiscard]] decltype(auto) get(const entity_type entt) const { ENTT_ASSERT(contains(entt)); - if constexpr(sizeof...(Component) == 1) { - return (std::get *>(pools)->get(entt), ...); + if constexpr(sizeof...(Component) == 0) { + return std::tuple_cat(get_as_tuple(*std::get *>(pools), entt)..., get_as_tuple(*std::get *>(pools), entt)...); + } else if constexpr(sizeof...(Component) == 1) { + return (std::get *>(pools)->get(entt), ...); } else { - return std::tuple({}))...>{get(entt)...}; + return std::tuple_cat(get_as_tuple(*std::get *>(pools), entt)...); } } @@ -6128,9 +9265,13 @@ public: */ template void each(Func func) const { - using owned_type_list = type_list_cat_t, type_list<>, type_list>...>; - using get_type_list = type_list_cat_t, type_list<>, type_list>...>; - traverse(std::move(func), owned_type_list{}, get_type_list{}); + for(auto args: each()) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, args); + } else { + std::apply([&func](auto, auto &&... less) { func(std::forward(less)...); }, args); + } + } } /** @@ -6146,8 +9287,8 @@ public: * * @return An iterable object to use to _visit_ the group. */ - [[nodiscard]] auto proxy() const ENTT_NOEXCEPT { - return group_proxy{pools, *length}; + [[nodiscard]] iterable_group each() const ENTT_NOEXCEPT { + return iterable_group{pools, length}; } /** @@ -6188,34 +9329,6186 @@ public: * @param args Arguments to forward to the sort function object, if any. */ template - void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { + void sort(Compare compare, Sort algo = Sort{}, Args &&... args) const { auto *cpool = std::get<0>(pools); - + if constexpr(sizeof...(Component) == 0) { static_assert(std::is_invocable_v, "Invalid comparison function"); - cpool->sort(cpool->end()-*length, cpool->end(), std::move(compare), std::move(algo), std::forward(args)...); + cpool->sort_n(*length, std::move(compare), std::move(algo), std::forward(args)...); } else if constexpr(sizeof...(Component) == 1) { - cpool->sort(cpool->end()-*length, cpool->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { - return compare((std::get *>(pools)->get(lhs), ...), (std::get *>(pools)->get(rhs), ...)); + cpool->sort_n(*length, [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { + return compare((std::get *>(pools)->get(lhs), ...), (std::get *>(pools)->get(rhs), ...)); }, std::move(algo), std::forward(args)...); } else { - cpool->sort(cpool->end()-*length, cpool->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { - return compare(std::tuple({}))...>{std::get *>(pools)->get(lhs)...}, std::tuple({}))...>{std::get *>(pools)->get(rhs)...}); + cpool->sort_n(*length, [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { + return compare(std::forward_as_tuple(std::get *>(pools)->get(lhs)...), std::forward_as_tuple(std::get *>(pools)->get(rhs)...)); }, std::move(algo), std::forward(args)...); } - + [this](auto *head, auto *... other) { for(auto next = *length; next; --next) { const auto pos = next - 1; [[maybe_unused]] const auto entt = head->data()[pos]; (other->swap(other->data()[pos], entt), ...); } - }(std::get *>(pools)...); + }(std::get *>(pools)...); } private: - const std::tuple *..., pool_type *...> pools; - const size_type *length; + const std::tuple *..., storage_type *...> pools; + const size_type * const length; +}; + + +} + + +#endif + +// #include "entity/handle.hpp" +#ifndef ENTT_ENTITY_HANDLE_HPP +#define ENTT_ENTITY_HANDLE_HPP + + +#include +#include +#include +// #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 +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/algorithm.hpp" + +// #include "../core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + + +#include +#include +// #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 "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +struct fnv1a_traits; + + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string { + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *curr) ENTT_NOEXCEPT: str{curr} {} + const Char *str; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT { + auto value = traits_type::offset; + + while(*curr != 0) { + value = (value ^ static_cast(*(curr++))) * traits_type::prime; + } + + return value; + } + +public: + /*! @brief Character type. */ + using value_type = Char; + /*! @brief Unsigned integer type. */ + using hash_type = id_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { + id_type partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + + /** + * @brief Returns directly the numeric representation of a string. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * const auto value = basic_hashed_string::to_value("my.png"); + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + return helper(str); + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { + return helper(wrapper.str); + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_NOEXCEPT + : str{nullptr}, hash{} + {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * basic_hashed_string hs{"my.png"}; + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param curr Human-readable identifer. + */ + template + constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT + : str{curr}, hash{helper(curr)} + {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + : str{wrapper.str}, hash{helper(wrapper.str)} + {} + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the instance. + */ + [[nodiscard]] constexpr const value_type * data() const ENTT_NOEXCEPT { + return str; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { + return hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { return data(); } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } + + /** + * @brief Compares two hashed strings. + * @param other Hashed string with which to compare. + * @return True if the two hashed strings are identical, false otherwise. + */ + [[nodiscard]] constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { + return hash == other.hash; + } + +private: + const value_type *str; + hash_type hash; +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the character type of the hashed string directly from a + * human-readable identifer provided to the constructor. + * + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + */ +template +basic_hashed_string(const Char (&str)[N]) +-> basic_hashed_string; + + +/** + * @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 +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + + +inline namespace literals { + + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_string{str}; +} + + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_wstring{str}; +} + + +} + + +} + + +#endif + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct ENTT_API type_seq final { + [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + + +template +[[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().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + + +template +[[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()); + return value; +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_seq final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() ENTT_NOEXCEPT { + static const id_type value = internal::type_seq::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } +}; + + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +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(0); +#else + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_seq::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 +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(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } +}; + + +/*! @brief Implementation specific information about a type. */ +class type_info final { + template + friend type_info type_id() ENTT_NOEXCEPT; + + type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT + : seq_value{seq_v}, + hash_value{hash_v}, + name_value{name_v} + {} + +public: + /*! @brief Default constructor. */ + type_info() ENTT_NOEXCEPT + : type_info({}, {}, {}) + {} + + /*! @brief Default copy constructor. */ + type_info(const type_info &) ENTT_NOEXCEPT = default; + /*! @brief Default move constructor. */ + type_info(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Default copy assignment operator. + * @return This type info object. + */ + type_info & operator=(const type_info &) ENTT_NOEXCEPT = default; + + /** + * @brief Default move assignment operator. + * @return This type info object. + */ + type_info & operator=(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Checks if a type info object is properly initialized. + * @return True if the object is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return name_value.data() != nullptr; + } + + /** + * @brief Type sequential identifier. + * @return Type sequential identifier. + */ + [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { + return seq_value; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { + return hash_value; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { + return name_value; + } + + /** + * @brief Compares the contents of two type info objects. + * @param other Object with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const type_info &other) const ENTT_NOEXCEPT { + return hash_value == other.hash_value; + } + +private: + id_type seq_value; + id_type hash_value; + std::string_view name_value; +}; + + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two contents differ, false otherwise. + */ +[[nodiscard]] inline bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/** + * @brief Returns the type info object for a given type. + * @tparam Type Type for which to generate a type info object. + * @return The type info object for the given type. + */ +template +[[nodiscard]] type_info type_id() ENTT_NOEXCEPT { + return type_info{ + type_seq>>::value(), + type_hash>>::value(), + type_name>>::value() + }; +} + + +} + + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // Unfortunately, doxygen cannot parse such a construct. + /*! @cond TURN_OFF_DOXYGEN */ + : choice_t + /*! @endcond */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t 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 +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::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 +struct size_of: std::integral_constant {}; + + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr auto size_of_v = size_of::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 +using unpack_as_t = Type; + + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_v = Value; + + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +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 +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 +struct type_list_element> + : type_list_element> +{}; + + +/** + * @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 +struct type_list_element<0u, type_list> { + /*! @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 +using type_list_element_t = typename type_list_element::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 +constexpr type_list operator+(type_list, type_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + std::disjunction_v...>, + typename type_list_unique>::type, + type_list_cat_t, typename type_list_unique>::type> + >; +}; + + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = type_list<>; +}; + + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::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 +struct type_list_contains; + + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other>: std::disjunction...> {}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr auto type_list_contains_v = type_list_contains::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +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 +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 +struct value_list_element> + : value_list_element> +{}; + + +/** + * @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 +struct value_list_element<0u, value_list> { + /*! @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 +inline constexpr auto value_list_element_v = value_list_element::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 +constexpr value_list operator+(value_list, value_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +[[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) +-> decltype(std::declval() == std::declval()) { return true; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>); +} + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template +struct is_equality_comparable: std::bool_constant(choice<2>)> {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** +* @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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr auto is_applicable_v = is_applicable::value; + + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +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 +struct is_applicable_r>: std::is_invocable_r {}; + + +/** + * @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 +inline constexpr auto is_applicable_r_v = is_applicable_r::value; + + +/** +* @brief Provides the member constant `value` to true if a given type is +* complete, false otherwise. +* @tparam Type Potential complete type. +*/ +template +struct is_complete: std::false_type {}; + + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + + +/** +* @brief Helper variable template. +* @tparam Type Potential complete type. +*/ +template +inline constexpr auto is_complete_v = is_complete::value; + + +/** + * @brief Provides the member constant `value` to true if a given type is empty + * and the empty type optimization is enabled, false otherwise. + * @tparam Type Potential empty type. + */ +template +struct is_empty: ENTT_IS_EMPTY(Type) {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potential empty type. + */ +template +inline constexpr auto is_empty_v = is_empty::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 +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::add_const_t; +}; + + +/** + * @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 +using constness_as_t = typename constness_as::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 +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class * clazz(Ret(Class:: *)(Args...)); + + template + static Class * clazz(Ret(Class:: *)(Args...) const); + + template + static Class * clazz(Type Class:: *); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + + +} + + +#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 +class basic_any { + enum class operation { COPY, MOVE, DTOR, COMP, ADDR, CADDR, REF, CREF, TYPE }; + + using storage_type = std::aligned_storage_t; + using vtable_type = const void *(const operation, const basic_any &, const void *); + + template + static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; + + template + [[nodiscard]] static bool compare(const void *lhs, const void *rhs) { + if constexpr(!std::is_function_v && is_equality_comparable_v) { + return *static_cast(lhs) == *static_cast(rhs); + } else { + return lhs == rhs; + } + } + + template + static Type & as(const void *to) { + return *const_cast(static_cast(to)); + } + + template + static const void * basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &from, [[maybe_unused]] const void *to) { + if constexpr(std::is_void_v) { + switch(op) { + case operation::COPY: + case operation::MOVE: + case operation::REF: + case operation::CREF: + as(to).vtable = from.vtable; + break; + default: + break; + } + } else if constexpr(std::is_lvalue_reference_v) { + using base_type = std::decay_t; + + switch(op) { + case operation::COPY: + if constexpr(std::is_copy_constructible_v) { + as(to) = *static_cast(from.instance); + } + break; + case operation::MOVE: + as(to).instance = from.instance; + as(to).vtable = from.vtable; + [[fallthrough]]; + case operation::DTOR: + break; + case operation::COMP: + return compare(from.instance, to) ? to : nullptr; + case operation::ADDR: + return std::is_const_v> ? nullptr : from.instance; + case operation::CADDR: + return from.instance; + case operation::REF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::CREF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::TYPE: + as(to) = type_id(); + break; + } + } else if constexpr(in_situ) { + #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L + auto *instance = const_cast(std::launder(reinterpret_cast(&from.storage))); + #else + auto *instance = const_cast(reinterpret_cast(&from.storage)); + #endif + + switch(op) { + case operation::COPY: + if constexpr(std::is_copy_constructible_v) { + new (&as(to).storage) Type{std::as_const(*instance)}; + as(to).vtable = from.vtable; + } + break; + case operation::MOVE: + new (&as(to).storage) Type{std::move(*instance)}; + as(to).vtable = from.vtable; + break; + case operation::DTOR: + instance->~Type(); + break; + case operation::COMP: + return compare(instance, to) ? to : nullptr; + case operation::ADDR: + case operation::CADDR: + return instance; + case operation::REF: + as(to).instance = instance; + as(to).vtable = basic_vtable; + break; + case operation::CREF: + as(to).instance = instance; + as(to).vtable = basic_vtable; + break; + case operation::TYPE: + as(to) = type_id(); + break; + } + } else { + switch(op) { + case operation::COPY: + if constexpr(std::is_copy_constructible_v) { + as(to).instance = new Type{*static_cast(from.instance)}; + as(to).vtable = from.vtable; + } + break; + case operation::MOVE: + as(to).instance = std::exchange(as(&from).instance, nullptr); + as(to).vtable = from.vtable; + break; + case operation::DTOR: + if constexpr(std::is_array_v) { + delete[] static_cast(from.instance); + } else { + delete static_cast(from.instance); + } + break; + case operation::COMP: + return compare(from.instance, to) ? to : nullptr; + case operation::ADDR: + case operation::CADDR: + return from.instance; + case operation::REF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::CREF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::TYPE: + as(to) = type_id(); + break; + } + } + + return nullptr; + } + + template + void initialize([[maybe_unused]] Args &&... args) { + if constexpr(!std::is_void_v) { + if constexpr(std::is_lvalue_reference_v) { + static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); + instance = (std::addressof(args), ...); + } else if constexpr(in_situ) { + if constexpr(std::is_aggregate_v) { + new (&storage) Type{std::forward(args)...}; + } else { + new (&storage) Type(std::forward(args)...); + } + } else { + if constexpr(std::is_aggregate_v) { + instance = new Type{std::forward(args)...}; + } else { + instance = new Type(std::forward(args)...); + } + } + } + } + +public: + /*! @brief Default constructor. */ + basic_any() ENTT_NOEXCEPT + : basic_any{std::in_place_type} + {} + + /** + * @brief Constructs an any by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&... args) + : instance{}, + vtable{&basic_vtable} + { + initialize(std::forward(args)...); + } + + /** + * @brief Constructs an any that holds an unmanaged object. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template + basic_any(std::reference_wrapper value) ENTT_NOEXCEPT + : basic_any{std::in_place_type, value.get()} + {} + + /** + * @brief Constructs an any from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, basic_any>>> + basic_any(Type &&value) + : basic_any{std::in_place_type>, std::forward(value)} + {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + basic_any(const basic_any &other) + : basic_any{std::in_place_type} + { + other.vtable(operation::COPY, other, this); + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_any(basic_any &&other) ENTT_NOEXCEPT + : basic_any{std::in_place_type} + { + other.vtable(operation::MOVE, other, this); + } + + /*! @brief Frees the internal storage, whatever it means. */ + ~basic_any() { + vtable(operation::DTOR, *this, nullptr); + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ + basic_any & operator=(const basic_any &other) { + vtable(operation::DTOR, *this, nullptr); + 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) { + vtable(operation::DTOR, *this, nullptr); + other.vtable(operation::MOVE, other, this); + 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 + basic_any & operator=(std::reference_wrapper value) ENTT_NOEXCEPT { + emplace(value.get()); + 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 + std::enable_if_t, basic_any>, basic_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns the type of the contained object. + * @return The type of the contained object, if any. + */ + [[nodiscard]] type_info type() const ENTT_NOEXCEPT { + type_info info{}; + vtable(operation::TYPE, *this, &info); + return info; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void * data() const ENTT_NOEXCEPT { + return vtable(operation::CADDR, *this, nullptr); + } + + /*! @copydoc data */ + [[nodiscard]] void * data() ENTT_NOEXCEPT { + return const_cast(vtable(operation::ADDR, *this, nullptr)); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + void emplace(Args &&... args) { + std::exchange(vtable, &basic_vtable)(operation::DTOR, *this, nullptr); + initialize(std::forward(args)...); + } + + /*! @brief Destroys contained object */ + void reset() { + std::exchange(vtable, &basic_vtable)(operation::DTOR, *this, nullptr); + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(vtable(operation::CADDR, *this, nullptr) == nullptr); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ + bool operator==(const basic_any &other) const ENTT_NOEXCEPT { + return type() == other.type() && (vtable(operation::COMP, *this, other.data()) == other.data()); + } + + /** + * @brief Aliasing constructor. + * @return An any that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT { + basic_any ref{}; + vtable(operation::REF, *this, &ref); + return ref; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT { + basic_any ref{}; + vtable(operation::CREF, *this, &ref); + return ref; + } + +private: + union { const void *instance; storage_type storage; }; + vtable_type *vtable; +}; + + +/** + * @brief Checks if two wrappers differ in their content. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param lhs A wrapper, either empty or not. + * @param rhs A wrapper, either empty or not. + * @return True if the two wrappers differ in their content, false otherwise. + */ +template +[[nodiscard]] inline bool operator!=(const basic_any &lhs, const basic_any &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 +Type any_cast(const basic_any &data) ENTT_NOEXCEPT { + const auto * const instance = any_cast>(&data); + ENTT_ASSERT(instance); + return static_cast(*instance); +} + + +/*! @copydoc any_cast */ +template +Type any_cast(basic_any &data) ENTT_NOEXCEPT { + // forces const on non-reference types to make them work also with wrappers for const references + auto * const instance = any_cast>(&data); + ENTT_ASSERT(instance); + return static_cast(*instance); +} + + +/*! @copydoc any_cast */ +template +Type any_cast(basic_any &&data) ENTT_NOEXCEPT { + // forces const on non-reference types to make them work also with wrappers for const references + auto * const instance = any_cast>(&data); + ENTT_ASSERT(instance); + return static_cast(std::move(*instance)); +} + + +/*! @copydoc any_cast */ +template +const Type * any_cast(const basic_any *data) ENTT_NOEXCEPT { + return (data->type() == type_id() ? static_cast(data->data()) : nullptr); +} + + +/*! @copydoc any_cast */ +template +Type * any_cast(basic_any *data) ENTT_NOEXCEPT { + // last attempt to make wrappers for const references return their values + return (data->type() == type_id() ? static_cast(static_cast, Type> *>(data)->data()) : nullptr); +} + + +} + + +#endif + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "hashed_string.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct ENTT_API type_seq final { + [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + + +template +[[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().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + + +template +[[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()); + return value; +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_seq final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() ENTT_NOEXCEPT { + static const id_type value = internal::type_seq::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } +}; + + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +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(0); +#else + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_seq::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 +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(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } +}; + + +/*! @brief Implementation specific information about a type. */ +class type_info final { + template + friend type_info type_id() ENTT_NOEXCEPT; + + type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT + : seq_value{seq_v}, + hash_value{hash_v}, + name_value{name_v} + {} + +public: + /*! @brief Default constructor. */ + type_info() ENTT_NOEXCEPT + : type_info({}, {}, {}) + {} + + /*! @brief Default copy constructor. */ + type_info(const type_info &) ENTT_NOEXCEPT = default; + /*! @brief Default move constructor. */ + type_info(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Default copy assignment operator. + * @return This type info object. + */ + type_info & operator=(const type_info &) ENTT_NOEXCEPT = default; + + /** + * @brief Default move assignment operator. + * @return This type info object. + */ + type_info & operator=(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Checks if a type info object is properly initialized. + * @return True if the object is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return name_value.data() != nullptr; + } + + /** + * @brief Type sequential identifier. + * @return Type sequential identifier. + */ + [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { + return seq_value; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { + return hash_value; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { + return name_value; + } + + /** + * @brief Compares the contents of two type info objects. + * @param other Object with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const type_info &other) const ENTT_NOEXCEPT { + return hash_value == other.hash_value; + } + +private: + id_type seq_value; + id_type hash_value; + std::string_view name_value; +}; + + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two contents differ, false otherwise. + */ +[[nodiscard]] inline bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/** + * @brief Returns the type info object for a given type. + * @tparam Type Type for which to generate a type info object. + * @return The type info object for the given type. + */ +template +[[nodiscard]] type_info type_id() ENTT_NOEXCEPT { + return type_info{ + type_seq>>::value(), + type_hash>>::value(), + type_name>>::value() + }; +} + + +} + + +#endif + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "group.hpp" +#ifndef ENTT_ENTITY_GROUP_HPP +#define ENTT_ENTITY_GROUP_HPP + + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + +// #include "utility.hpp" + + + +namespace entt { + + +/** + * @brief Group. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +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.
+ * Moreover, sorting a non-owning group affects all the instances of the same + * group (it means that users don't have to call `sort` on each instance to sort + * all of them because they _share_ entities and components). + * + * @warning + * Lifetime of a group must not overcome that of the registry that generated it. + * In any other case, attempting to use a group results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Exclude Types of components used to filter the group. + * @tparam Get Type of components observed by the group. + */ +template +class basic_group, get_t> final { + /*! @brief A registry is allowed to create groups. */ + friend class basic_registry; + + template + using storage_type = constness_as_t>::storage_type, Component>; + + class iterable_group final { + friend class basic_group, get_t>; + + template + class iterable_group_iterator final { + friend class iterable_group; + + template + iterable_group_iterator(It from, const std::tuple *...> &args) ENTT_NOEXCEPT + : it{from}, + pools{args} + {} + + public: + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); + using pointer = void; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + + iterable_group_iterator & operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + iterable_group_iterator operator++(int) ENTT_NOEXCEPT { + iterable_group_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + const auto entt = *it; + return std::tuple_cat(std::make_tuple(entt), get_as_tuple(*std::get *>(pools), entt)...); + } + + [[nodiscard]] bool operator==(const iterable_group_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + [[nodiscard]] bool operator!=(const iterable_group_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + private: + It it; + std::tuple *...> pools; + }; + + iterable_group(basic_sparse_set * const ref, const std::tuple *...> &cpools) + : handler{ref}, + pools{cpools} + {} + + public: + using iterator = iterable_group_iterator::iterator>; + using reverse_iterator = iterable_group_iterator::reverse_iterator>; + + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return handler ? iterator{handler->begin(), pools} : iterator{{}, pools}; + } + + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return handler ? iterator{handler->end(), pools} : iterator{{}, pools}; + } + + [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return handler ? reverse_iterator{handler->rbegin(), pools} : reverse_iterator{{}, pools}; + } + + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return handler ? reverse_iterator{handler->rend(), pools} : reverse_iterator{{}, pools}; + } + + private: + basic_sparse_set * const handler; + const std::tuple *...> pools; + }; + + basic_group(basic_sparse_set &ref, storage_type &... gpool) ENTT_NOEXCEPT + : handler{&ref}, + pools{&gpool...} + {} + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = typename basic_sparse_set::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename basic_sparse_set::reverse_iterator; + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() ENTT_NOEXCEPT + : handler{} + {} + + /** + * @brief Returns the number of entities that have the given components. + * @return Number of entities that have the given components. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return *this ? handler->size() : size_type{}; + } + + /** + * @brief Returns the number of elements that a group has currently + * allocated space for. + * @return Capacity of the group. + */ + [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { + return *this ? handler->capacity() : size_type{}; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + if(*this) { + handler->shrink_to_fit(); + } + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return !*this || handler->empty(); + } + + /** + * @brief Direct access to the list of entities. + * + * The returned pointer is such that range `[data(), data() + size())` is + * always a valid range, even if the container is empty. + * + * @return A pointer to the array of entities. + */ + [[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT { + return *this ? handler->data() : nullptr; + } + + /** + * @brief Returns an iterator to the first entity of the group. + * + * The returned iterator points to the first entity of the group. If the + * group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return *this ? handler->begin() : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * + * The returned iterator points to the entity following the last entity of + * the group. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return *this ? handler->end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * The returned iterator points to the first entity of the reversed group. + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return *this ? handler->rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * + * The returned iterator points to the entity following the last entity of + * the reversed group. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return *this ? handler->rend() : reverse_iterator{}; + } + + /** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid entity identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const { + const auto it = *this ? handler->find(entt) : iterator{}; + return it != end() && *it == entt ? it : end(); + } + + /** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return handler != nullptr; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid entity identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const { + return *this && handler->contains(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * + * Prefer this function instead of `registry::get` during iterations. It has + * far better performance than its counterpart. + * + * @warning + * Attempting to use an invalid component type results in a compilation + * error. Attempting to use an entity that doesn't belong to the group + * results in undefined behavior. + * + * @tparam Component Types of components to get. + * @param entt A valid entity identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + ENTT_ASSERT(contains(entt)); + + if constexpr(sizeof...(Component) == 0) { + return std::tuple_cat(get_as_tuple(*std::get *>(pools), entt)...); + } else if constexpr(sizeof...(Component) == 1) { + return (std::get *>(pools)->get(entt), ...); + } else { + return std::tuple_cat(get_as_tuple(*std::get *>(pools), entt)...); + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.
+ * 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 + void each(Func func) const { + for(const auto entt: *this) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); + } else { + std::apply(func, get(entt)); + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ the group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] iterable_group each() const ENTT_NOEXCEPT { + return iterable_group{handler, pools}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * Sort the group so that iterating it with a couple of iterators returns + * entities and components in the expected order. See `begin` and `end` for + * more details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple, std::tuple); + * bool(const Component &..., const Component &...); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Component` are such that they are iterated by the group.
+ * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function oject must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Component Optional types of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { + if(*this) { + if constexpr(sizeof...(Component) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + handler->sort(std::move(compare), std::move(algo), std::forward(args)...); + } else if constexpr(sizeof...(Component) == 1) { + handler->sort([this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { + return compare((std::get *>(pools)->get(lhs), ...), (std::get *>(pools)->get(rhs), ...)); + }, std::move(algo), std::forward(args)...); + } else { + handler->sort([this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { + return compare(std::forward_as_tuple(std::get *>(pools)->get(lhs)...), std::forward_as_tuple(std::get *>(pools)->get(rhs)...)); + }, std::move(algo), std::forward(args)...); + } + } + } + + /** + * @brief Sort the shared pool of entities according to the given component. + * + * Non-owning groups of the same type share with the registry a pool of + * entities with its own order that doesn't depend on the order of any pool + * of components. Users can order the underlying data structure so that it + * respects the order of the pool of the given component. + * + * @note + * The shared pool of entities and thus its order is affected by the changes + * to each and every pool that it tracks. Therefore changes to those pools + * can quickly ruin the order imposed to the pool of entities shared between + * the non-owning groups. + * + * @tparam Component Type of component to use to impose the order. + */ + template + void sort() const { + if(*this) { + handler->respect(*std::get *>(pools)); + } + } + +private: + basic_sparse_set * const handler; + const std::tuple *...> pools; +}; + + +/** + * @brief Owning group. + * + * Owning groups return all entities and only the entities that have at least + * the given components. Moreover: + * + * * It's guaranteed that the entity list is tightly packed in memory for fast + * iterations. + * * It's guaranteed that the lists of owned components are tightly packed in + * memory for even faster iterations and to allow direct access. + * * They stay true to the order of the owned components and all instances have + * the same order in memory. + * + * The more types of components are owned by a group, the faster it is to + * iterate them. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Groups share references to the underlying data structures of the registry + * that generated them. Therefore any change to the entities and to the + * components made by means of the registry are immediately reflected by all the + * groups. + * Moreover, sorting an owning group affects all the instance of the same group + * (it means that users don't have to call `sort` on each instance to sort all + * of them because they share the underlying data structure). + * + * @warning + * Lifetime of a group must not overcome that of the registry that generated it. + * In any other case, attempting to use a group results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Exclude Types of components used to filter the group. + * @tparam Get Types of components observed by the group. + * @tparam Owned Types of components owned by the group. + */ +template +class basic_group, get_t, Owned...> final { + /*! @brief A registry is allowed to create groups. */ + friend class basic_registry; + + template + using storage_type = constness_as_t>::storage_type, Component>; + + class iterable_group final { + friend class basic_group, get_t, Owned...>; + + template + class iterable_group_iterator; + + template + class iterable_group_iterator> final { + friend class iterable_group; + + template + iterable_group_iterator(It from, const std::tuple &other, const std::tuple *...> &cpools) ENTT_NOEXCEPT + : it{from}, + owned{std::get(other)...}, + get{cpools} + {} + + public: + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); + using pointer = void; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + + iterable_group_iterator & operator++() ENTT_NOEXCEPT { + return ++it, (++std::get(owned), ...), *this; + } + + iterable_group_iterator operator++(int) ENTT_NOEXCEPT { + iterable_group_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return std::tuple_cat( + std::make_tuple(*it), + std::forward_as_tuple(*std::get(owned)...), + get_as_tuple(*std::get *>(get), *it)... + ); + } + + [[nodiscard]] bool operator==(const iterable_group_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + [[nodiscard]] bool operator!=(const iterable_group_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + private: + It it; + std::tuple owned; + std::tuple *...> get; + }; + + iterable_group(std::tuple *..., storage_type *...> cpools, const std::size_t * const extent) + : pools{cpools}, + length{extent} + {} + + public: + using iterator = iterable_group_iterator< + typename basic_sparse_set::iterator, + type_list_cat_t>().get({}))>, type_list<>, type_list>().end())>>...> + >; + using reverse_iterator = iterable_group_iterator< + typename basic_sparse_set::reverse_iterator, + type_list_cat_t>().get({}))>, type_list<>, type_list>().rbegin())>>...> + >; + + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return length ? iterator{ + std::get<0>(pools)->basic_sparse_set::end() - *length, + std::make_tuple((std::get *>(pools)->end() - *length)...), + std::make_tuple(std::get *>(pools)...) + } : iterator{{}, std::make_tuple(decltype(std::get *>(pools)->end()){}...), std::make_tuple(std::get *>(pools)...)}; + } + + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return length ? iterator{ + std::get<0>(pools)->basic_sparse_set::end(), + std::make_tuple((std::get *>(pools)->end())...), + std::make_tuple(std::get *>(pools)...) + } : iterator{{}, std::make_tuple(decltype(std::get *>(pools)->end()){}...), std::make_tuple(std::get *>(pools)...)}; + } + + [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return length ? reverse_iterator{ + std::get<0>(pools)->basic_sparse_set::rbegin(), + std::make_tuple((std::get *>(pools)->rbegin())...), + std::make_tuple(std::get *>(pools)...) + } : reverse_iterator{{}, std::make_tuple(decltype(std::get *>(pools)->rbegin()){}...), std::make_tuple(std::get *>(pools)...)}; + } + + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return length ? reverse_iterator{ + std::get<0>(pools)->basic_sparse_set::rbegin() + *length, + std::make_tuple((std::get *>(pools)->rbegin() + *length)...), + std::make_tuple(std::get *>(pools)...) + } : reverse_iterator{{}, std::make_tuple(decltype(std::get *>(pools)->rbegin()){}...), std::make_tuple(std::get *>(pools)...)}; + } + + private: + const std::tuple *..., storage_type *...> pools; + const std::size_t * const length; + }; + + basic_group(const std::size_t &extent, storage_type &... opool, storage_type &... gpool) ENTT_NOEXCEPT + : pools{&opool..., &gpool...}, + length{&extent} + {} + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = typename basic_sparse_set::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename basic_sparse_set::reverse_iterator; + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() ENTT_NOEXCEPT + : length{} + {} + + /** + * @brief Returns the number of entities that have the given components. + * @return Number of entities that have the given components. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return *this ? *length : size_type{}; + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return !*this || !*length; + } + + /** + * @brief Direct access to the list of components of a given pool. + * + * The returned pointer is such that range + * `[raw(), raw() + size())` is always a valid range, + * even if the container is empty.
+ * + * @warning + * This function is only available for owned types. + * + * @tparam Component Type of component in which one is interested. + * @return A pointer to the array of components. + */ + template + [[nodiscard]] Component * raw() const ENTT_NOEXCEPT { + static_assert((std::is_same_v || ...), "Non-owned type"); + auto *cpool = std::get *>(pools); + return cpool ? cpool->raw() : nullptr; + } + + /** + * @brief Direct access to the list of entities. + * + * The returned pointer is such that range `[data(), data() + size())` is + * always a valid range, even if the container is empty. + * + * @return A pointer to the array of entities. + */ + [[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT { + return *this ? std::get<0>(pools)->data() : nullptr; + } + + /** + * @brief Returns an iterator to the first entity of the group. + * + * The returned iterator points to the first entity of the group. If the + * group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return *this ? (std::get<0>(pools)->basic_sparse_set::end() - *length) : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * + * The returned iterator points to the entity following the last entity of + * the group. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return *this ? std::get<0>(pools)->basic_sparse_set::end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * The returned iterator points to the first entity of the reversed group. + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return *this ? std::get<0>(pools)->basic_sparse_set::rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * + * The returned iterator points to the entity following the last entity of + * the reversed group. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return *this ? (std::get<0>(pools)->basic_sparse_set::rbegin() + *length) : reverse_iterator{}; + } + + /** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid entity identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const { + const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{}; + return it != end() && it >= begin() && *it == entt ? it : end(); + } + + /** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return length != nullptr; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid entity identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const { + return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length)); + } + + /** + * @brief Returns the components assigned to the given entity. + * + * Prefer this function instead of `registry::get` during iterations. It has + * far better performance than its counterpart. + * + * @warning + * Attempting to use an invalid component type results in a compilation + * error. Attempting to use an entity that doesn't belong to the group + * results in undefined behavior. + * + * @tparam Component Types of components to get. + * @param entt A valid entity identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + ENTT_ASSERT(contains(entt)); + + if constexpr(sizeof...(Component) == 0) { + return std::tuple_cat(get_as_tuple(*std::get *>(pools), entt)..., get_as_tuple(*std::get *>(pools), entt)...); + } else if constexpr(sizeof...(Component) == 1) { + return (std::get *>(pools)->get(entt), ...); + } else { + return std::tuple_cat(get_as_tuple(*std::get *>(pools), entt)...); + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.
+ * 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 + void each(Func func) const { + for(auto args: each()) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, args); + } else { + std::apply([&func](auto, auto &&... less) { func(std::forward(less)...); }, args); + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ the group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] iterable_group each() const ENTT_NOEXCEPT { + return iterable_group{pools, length}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * Sort the group so that iterating it with a couple of iterators returns + * entities and components in the expected order. See `begin` and `end` for + * more details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple, std::tuple); + * 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.
+ * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function oject must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Component Optional types of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&... args) const { + auto *cpool = std::get<0>(pools); + + if constexpr(sizeof...(Component) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + cpool->sort_n(*length, std::move(compare), std::move(algo), std::forward(args)...); + } else if constexpr(sizeof...(Component) == 1) { + cpool->sort_n(*length, [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { + return compare((std::get *>(pools)->get(lhs), ...), (std::get *>(pools)->get(rhs), ...)); + }, std::move(algo), std::forward(args)...); + } else { + cpool->sort_n(*length, [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { + return compare(std::forward_as_tuple(std::get *>(pools)->get(lhs)...), std::forward_as_tuple(std::get *>(pools)->get(rhs)...)); + }, std::move(algo), std::forward(args)...); + } + + [this](auto *head, auto *... other) { + for(auto next = *length; next; --next) { + const auto pos = next - 1; + [[maybe_unused]] const auto entt = head->data()[pos]; + (other->swap(other->data()[pos], entt), ...); + } + }(std::get *>(pools)...); + } + +private: + const std::tuple *..., storage_type *...> pools; + const size_type * const length; +}; + + +} + + +#endif + +// #include "poly_storage.hpp" +#ifndef ENTT_ENTITY_POLY_STORAGE_HPP +#define ENTT_ENTITY_POLY_STORAGE_HPP + + +#include +#include +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../poly/poly.hpp" +#ifndef ENTT_POLY_POLY_HPP +#define ENTT_POLY_POLY_HPP + + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifdef ENTT_PAGE_SIZE +static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); +#else +# define ENTT_PAGE_SIZE 4096 +#endif + + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#endif + +// #include "../core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifdef ENTT_PAGE_SIZE +static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); +#else +# define ENTT_PAGE_SIZE 4096 +#endif + + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +#include +// #include "../config/config.h" + + + +namespace entt { + + +template)> +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; + + +} + + +#endif + +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + + +#include +#include +// #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 "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +struct fnv1a_traits; + + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string { + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *curr) ENTT_NOEXCEPT: str{curr} {} + const Char *str; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT { + auto value = traits_type::offset; + + while(*curr != 0) { + value = (value ^ static_cast(*(curr++))) * traits_type::prime; + } + + return value; + } + +public: + /*! @brief Character type. */ + using value_type = Char; + /*! @brief Unsigned integer type. */ + using hash_type = id_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { + id_type partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + + /** + * @brief Returns directly the numeric representation of a string. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * const auto value = basic_hashed_string::to_value("my.png"); + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + return helper(str); + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { + return helper(wrapper.str); + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_NOEXCEPT + : str{nullptr}, hash{} + {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * basic_hashed_string hs{"my.png"}; + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param curr Human-readable identifer. + */ + template + constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT + : str{curr}, hash{helper(curr)} + {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + : str{wrapper.str}, hash{helper(wrapper.str)} + {} + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the instance. + */ + [[nodiscard]] constexpr const value_type * data() const ENTT_NOEXCEPT { + return str; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { + return hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { return data(); } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } + + /** + * @brief Compares two hashed strings. + * @param other Hashed string with which to compare. + * @return True if the two hashed strings are identical, false otherwise. + */ + [[nodiscard]] constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { + return hash == other.hash; + } + +private: + const value_type *str; + hash_type hash; +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the character type of the hashed string directly from a + * human-readable identifer provided to the constructor. + * + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + */ +template +basic_hashed_string(const Char (&str)[N]) +-> basic_hashed_string; + + +/** + * @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 +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + + +inline namespace literals { + + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_string{str}; +} + + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_wstring{str}; +} + + +} + + +} + + +#endif + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct ENTT_API type_seq final { + [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + + +template +[[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().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + + +template +[[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()); + return value; +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_seq final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() ENTT_NOEXCEPT { + static const id_type value = internal::type_seq::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } +}; + + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +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(0); +#else + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_seq::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 +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(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } +}; + + +/*! @brief Implementation specific information about a type. */ +class type_info final { + template + friend type_info type_id() ENTT_NOEXCEPT; + + type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT + : seq_value{seq_v}, + hash_value{hash_v}, + name_value{name_v} + {} + +public: + /*! @brief Default constructor. */ + type_info() ENTT_NOEXCEPT + : type_info({}, {}, {}) + {} + + /*! @brief Default copy constructor. */ + type_info(const type_info &) ENTT_NOEXCEPT = default; + /*! @brief Default move constructor. */ + type_info(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Default copy assignment operator. + * @return This type info object. + */ + type_info & operator=(const type_info &) ENTT_NOEXCEPT = default; + + /** + * @brief Default move assignment operator. + * @return This type info object. + */ + type_info & operator=(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Checks if a type info object is properly initialized. + * @return True if the object is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return name_value.data() != nullptr; + } + + /** + * @brief Type sequential identifier. + * @return Type sequential identifier. + */ + [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { + return seq_value; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { + return hash_value; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { + return name_value; + } + + /** + * @brief Compares the contents of two type info objects. + * @param other Object with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const type_info &other) const ENTT_NOEXCEPT { + return hash_value == other.hash_value; + } + +private: + id_type seq_value; + id_type hash_value; + std::string_view name_value; +}; + + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two contents differ, false otherwise. + */ +[[nodiscard]] inline bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/** + * @brief Returns the type info object for a given type. + * @tparam Type Type for which to generate a type info object. + * @return The type info object for the given type. + */ +template +[[nodiscard]] type_info type_id() ENTT_NOEXCEPT { + return type_info{ + type_seq>>::value(), + type_hash>>::value(), + type_name>>::value() + }; +} + + +} + + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // Unfortunately, doxygen cannot parse such a construct. + /*! @cond TURN_OFF_DOXYGEN */ + : choice_t + /*! @endcond */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t 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 +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::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 +struct size_of: std::integral_constant {}; + + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr auto size_of_v = size_of::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 +using unpack_as_t = Type; + + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_v = Value; + + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +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 +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 +struct type_list_element> + : type_list_element> +{}; + + +/** + * @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 +struct type_list_element<0u, type_list> { + /*! @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 +using type_list_element_t = typename type_list_element::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 +constexpr type_list operator+(type_list, type_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + std::disjunction_v...>, + typename type_list_unique>::type, + type_list_cat_t, typename type_list_unique>::type> + >; +}; + + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = type_list<>; +}; + + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::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 +struct type_list_contains; + + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other>: std::disjunction...> {}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr auto type_list_contains_v = type_list_contains::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +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 +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 +struct value_list_element> + : value_list_element> +{}; + + +/** + * @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 +struct value_list_element<0u, value_list> { + /*! @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 +inline constexpr auto value_list_element_v = value_list_element::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 +constexpr value_list operator+(value_list, value_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +[[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) +-> decltype(std::declval() == std::declval()) { return true; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>); +} + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template +struct is_equality_comparable: std::bool_constant(choice<2>)> {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** +* @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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr auto is_applicable_v = is_applicable::value; + + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +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 +struct is_applicable_r>: std::is_invocable_r {}; + + +/** + * @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 +inline constexpr auto is_applicable_r_v = is_applicable_r::value; + + +/** +* @brief Provides the member constant `value` to true if a given type is +* complete, false otherwise. +* @tparam Type Potential complete type. +*/ +template +struct is_complete: std::false_type {}; + + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + + +/** +* @brief Helper variable template. +* @tparam Type Potential complete type. +*/ +template +inline constexpr auto is_complete_v = is_complete::value; + + +/** + * @brief Provides the member constant `value` to true if a given type is empty + * and the empty type optimization is enabled, false otherwise. + * @tparam Type Potential empty type. + */ +template +struct is_empty: ENTT_IS_EMPTY(Type) {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potential empty type. + */ +template +inline constexpr auto is_empty_v = is_empty::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 +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::add_const_t; +}; + + +/** + * @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 +using constness_as_t = typename constness_as::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 +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class * clazz(Ret(Class:: *)(Args...)); + + template + static Class * clazz(Ret(Class:: *)(Args...) const); + + template + static Class * clazz(Type Class:: *); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + + +} + + +#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 +class basic_any { + enum class operation { COPY, MOVE, DTOR, COMP, ADDR, CADDR, REF, CREF, TYPE }; + + using storage_type = std::aligned_storage_t; + using vtable_type = const void *(const operation, const basic_any &, const void *); + + template + static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; + + template + [[nodiscard]] static bool compare(const void *lhs, const void *rhs) { + if constexpr(!std::is_function_v && is_equality_comparable_v) { + return *static_cast(lhs) == *static_cast(rhs); + } else { + return lhs == rhs; + } + } + + template + static Type & as(const void *to) { + return *const_cast(static_cast(to)); + } + + template + static const void * basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &from, [[maybe_unused]] const void *to) { + if constexpr(std::is_void_v) { + switch(op) { + case operation::COPY: + case operation::MOVE: + case operation::REF: + case operation::CREF: + as(to).vtable = from.vtable; + break; + default: + break; + } + } else if constexpr(std::is_lvalue_reference_v) { + using base_type = std::decay_t; + + switch(op) { + case operation::COPY: + if constexpr(std::is_copy_constructible_v) { + as(to) = *static_cast(from.instance); + } + break; + case operation::MOVE: + as(to).instance = from.instance; + as(to).vtable = from.vtable; + [[fallthrough]]; + case operation::DTOR: + break; + case operation::COMP: + return compare(from.instance, to) ? to : nullptr; + case operation::ADDR: + return std::is_const_v> ? nullptr : from.instance; + case operation::CADDR: + return from.instance; + case operation::REF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::CREF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::TYPE: + as(to) = type_id(); + break; + } + } else if constexpr(in_situ) { + #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L + auto *instance = const_cast(std::launder(reinterpret_cast(&from.storage))); + #else + auto *instance = const_cast(reinterpret_cast(&from.storage)); + #endif + + switch(op) { + case operation::COPY: + if constexpr(std::is_copy_constructible_v) { + new (&as(to).storage) Type{std::as_const(*instance)}; + as(to).vtable = from.vtable; + } + break; + case operation::MOVE: + new (&as(to).storage) Type{std::move(*instance)}; + as(to).vtable = from.vtable; + break; + case operation::DTOR: + instance->~Type(); + break; + case operation::COMP: + return compare(instance, to) ? to : nullptr; + case operation::ADDR: + case operation::CADDR: + return instance; + case operation::REF: + as(to).instance = instance; + as(to).vtable = basic_vtable; + break; + case operation::CREF: + as(to).instance = instance; + as(to).vtable = basic_vtable; + break; + case operation::TYPE: + as(to) = type_id(); + break; + } + } else { + switch(op) { + case operation::COPY: + if constexpr(std::is_copy_constructible_v) { + as(to).instance = new Type{*static_cast(from.instance)}; + as(to).vtable = from.vtable; + } + break; + case operation::MOVE: + as(to).instance = std::exchange(as(&from).instance, nullptr); + as(to).vtable = from.vtable; + break; + case operation::DTOR: + if constexpr(std::is_array_v) { + delete[] static_cast(from.instance); + } else { + delete static_cast(from.instance); + } + break; + case operation::COMP: + return compare(from.instance, to) ? to : nullptr; + case operation::ADDR: + case operation::CADDR: + return from.instance; + case operation::REF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::CREF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::TYPE: + as(to) = type_id(); + break; + } + } + + return nullptr; + } + + template + void initialize([[maybe_unused]] Args &&... args) { + if constexpr(!std::is_void_v) { + if constexpr(std::is_lvalue_reference_v) { + static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); + instance = (std::addressof(args), ...); + } else if constexpr(in_situ) { + if constexpr(std::is_aggregate_v) { + new (&storage) Type{std::forward(args)...}; + } else { + new (&storage) Type(std::forward(args)...); + } + } else { + if constexpr(std::is_aggregate_v) { + instance = new Type{std::forward(args)...}; + } else { + instance = new Type(std::forward(args)...); + } + } + } + } + +public: + /*! @brief Default constructor. */ + basic_any() ENTT_NOEXCEPT + : basic_any{std::in_place_type} + {} + + /** + * @brief Constructs an any by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&... args) + : instance{}, + vtable{&basic_vtable} + { + initialize(std::forward(args)...); + } + + /** + * @brief Constructs an any that holds an unmanaged object. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template + basic_any(std::reference_wrapper value) ENTT_NOEXCEPT + : basic_any{std::in_place_type, value.get()} + {} + + /** + * @brief Constructs an any from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, basic_any>>> + basic_any(Type &&value) + : basic_any{std::in_place_type>, std::forward(value)} + {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + basic_any(const basic_any &other) + : basic_any{std::in_place_type} + { + other.vtable(operation::COPY, other, this); + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_any(basic_any &&other) ENTT_NOEXCEPT + : basic_any{std::in_place_type} + { + other.vtable(operation::MOVE, other, this); + } + + /*! @brief Frees the internal storage, whatever it means. */ + ~basic_any() { + vtable(operation::DTOR, *this, nullptr); + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ + basic_any & operator=(const basic_any &other) { + vtable(operation::DTOR, *this, nullptr); + 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) { + vtable(operation::DTOR, *this, nullptr); + other.vtable(operation::MOVE, other, this); + 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 + basic_any & operator=(std::reference_wrapper value) ENTT_NOEXCEPT { + emplace(value.get()); + 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 + std::enable_if_t, basic_any>, basic_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns the type of the contained object. + * @return The type of the contained object, if any. + */ + [[nodiscard]] type_info type() const ENTT_NOEXCEPT { + type_info info{}; + vtable(operation::TYPE, *this, &info); + return info; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void * data() const ENTT_NOEXCEPT { + return vtable(operation::CADDR, *this, nullptr); + } + + /*! @copydoc data */ + [[nodiscard]] void * data() ENTT_NOEXCEPT { + return const_cast(vtable(operation::ADDR, *this, nullptr)); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + void emplace(Args &&... args) { + std::exchange(vtable, &basic_vtable)(operation::DTOR, *this, nullptr); + initialize(std::forward(args)...); + } + + /*! @brief Destroys contained object */ + void reset() { + std::exchange(vtable, &basic_vtable)(operation::DTOR, *this, nullptr); + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(vtable(operation::CADDR, *this, nullptr) == nullptr); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ + bool operator==(const basic_any &other) const ENTT_NOEXCEPT { + return type() == other.type() && (vtable(operation::COMP, *this, other.data()) == other.data()); + } + + /** + * @brief Aliasing constructor. + * @return An any that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT { + basic_any ref{}; + vtable(operation::REF, *this, &ref); + return ref; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT { + basic_any ref{}; + vtable(operation::CREF, *this, &ref); + return ref; + } + +private: + union { const void *instance; storage_type storage; }; + vtable_type *vtable; +}; + + +/** + * @brief Checks if two wrappers differ in their content. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param lhs A wrapper, either empty or not. + * @param rhs A wrapper, either empty or not. + * @return True if the two wrappers differ in their content, false otherwise. + */ +template +[[nodiscard]] inline bool operator!=(const basic_any &lhs, const basic_any &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 +Type any_cast(const basic_any &data) ENTT_NOEXCEPT { + const auto * const instance = any_cast>(&data); + ENTT_ASSERT(instance); + return static_cast(*instance); +} + + +/*! @copydoc any_cast */ +template +Type any_cast(basic_any &data) ENTT_NOEXCEPT { + // forces const on non-reference types to make them work also with wrappers for const references + auto * const instance = any_cast>(&data); + ENTT_ASSERT(instance); + return static_cast(*instance); +} + + +/*! @copydoc any_cast */ +template +Type any_cast(basic_any &&data) ENTT_NOEXCEPT { + // forces const on non-reference types to make them work also with wrappers for const references + auto * const instance = any_cast>(&data); + ENTT_ASSERT(instance); + return static_cast(std::move(*instance)); +} + + +/*! @copydoc any_cast */ +template +const Type * any_cast(const basic_any *data) ENTT_NOEXCEPT { + return (data->type() == type_id() ? static_cast(data->data()) : nullptr); +} + + +/*! @copydoc any_cast */ +template +Type * any_cast(basic_any *data) ENTT_NOEXCEPT { + // last attempt to make wrappers for const references return their values + return (data->type() == type_id() ? static_cast(static_cast, Type> *>(data)->data()) : nullptr); +} + + +} + + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "hashed_string.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct ENTT_API type_seq final { + [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + + +template +[[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().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + + +template +[[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()); + return value; +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_seq final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() ENTT_NOEXCEPT { + static const id_type value = internal::type_seq::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } +}; + + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +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(0); +#else + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_seq::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 +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(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } +}; + + +/*! @brief Implementation specific information about a type. */ +class type_info final { + template + friend type_info type_id() ENTT_NOEXCEPT; + + type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT + : seq_value{seq_v}, + hash_value{hash_v}, + name_value{name_v} + {} + +public: + /*! @brief Default constructor. */ + type_info() ENTT_NOEXCEPT + : type_info({}, {}, {}) + {} + + /*! @brief Default copy constructor. */ + type_info(const type_info &) ENTT_NOEXCEPT = default; + /*! @brief Default move constructor. */ + type_info(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Default copy assignment operator. + * @return This type info object. + */ + type_info & operator=(const type_info &) ENTT_NOEXCEPT = default; + + /** + * @brief Default move assignment operator. + * @return This type info object. + */ + type_info & operator=(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Checks if a type info object is properly initialized. + * @return True if the object is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return name_value.data() != nullptr; + } + + /** + * @brief Type sequential identifier. + * @return Type sequential identifier. + */ + [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { + return seq_value; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { + return hash_value; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { + return name_value; + } + + /** + * @brief Compares the contents of two type info objects. + * @param other Object with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const type_info &other) const ENTT_NOEXCEPT { + return hash_value == other.hash_value; + } + +private: + id_type seq_value; + id_type hash_value; + std::string_view name_value; +}; + + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two contents differ, false otherwise. + */ +[[nodiscard]] inline bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/** + * @brief Returns the type info object for a given type. + * @tparam Type Type for which to generate a type info object. + * @return The type info object for the given type. + */ +template +[[nodiscard]] type_info type_id() ENTT_NOEXCEPT { + return type_info{ + type_seq>>::value(), + type_hash>>::value(), + type_name>>::value() + }; +} + + +} + + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // Unfortunately, doxygen cannot parse such a construct. + /*! @cond TURN_OFF_DOXYGEN */ + : choice_t + /*! @endcond */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t 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 +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::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 +struct size_of: std::integral_constant {}; + + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr auto size_of_v = size_of::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 +using unpack_as_t = Type; + + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_v = Value; + + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +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 +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 +struct type_list_element> + : type_list_element> +{}; + + +/** + * @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 +struct type_list_element<0u, type_list> { + /*! @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 +using type_list_element_t = typename type_list_element::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 +constexpr type_list operator+(type_list, type_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + std::disjunction_v...>, + typename type_list_unique>::type, + type_list_cat_t, typename type_list_unique>::type> + >; +}; + + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = type_list<>; +}; + + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::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 +struct type_list_contains; + + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other>: std::disjunction...> {}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr auto type_list_contains_v = type_list_contains::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +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 +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 +struct value_list_element> + : value_list_element> +{}; + + +/** + * @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 +struct value_list_element<0u, value_list> { + /*! @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 +inline constexpr auto value_list_element_v = value_list_element::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 +constexpr value_list operator+(value_list, value_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +[[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) +-> decltype(std::declval() == std::declval()) { return true; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>); +} + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template +struct is_equality_comparable: std::bool_constant(choice<2>)> {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** +* @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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr auto is_applicable_v = is_applicable::value; + + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +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 +struct is_applicable_r>: std::is_invocable_r {}; + + +/** + * @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 +inline constexpr auto is_applicable_r_v = is_applicable_r::value; + + +/** +* @brief Provides the member constant `value` to true if a given type is +* complete, false otherwise. +* @tparam Type Potential complete type. +*/ +template +struct is_complete: std::false_type {}; + + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + + +/** +* @brief Helper variable template. +* @tparam Type Potential complete type. +*/ +template +inline constexpr auto is_complete_v = is_complete::value; + + +/** + * @brief Provides the member constant `value` to true if a given type is empty + * and the empty type optimization is enabled, false otherwise. + * @tparam Type Potential empty type. + */ +template +struct is_empty: ENTT_IS_EMPTY(Type) {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potential empty type. + */ +template +inline constexpr auto is_empty_v = is_empty::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 +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::add_const_t; +}; + + +/** + * @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 +using constness_as_t = typename constness_as::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 +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class * clazz(Ret(Class:: *)(Args...)); + + template + static Class * clazz(Ret(Class:: *)(Args...) const); + + template + static Class * clazz(Type Class:: *); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + + +} + + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_POLY_FWD_HPP +#define ENTT_POLY_FWD_HPP + + +#include + + +namespace entt { + + +template)> +class basic_poly; + + +/** + * @brief Alias declaration for the most common use case. + * @tparam Concept Concept descriptor. + */ +template +using poly = basic_poly; + + +} + + +#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 + 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 + poly_inspector invoke(Args &&... args) const; + + /*! @copydoc invoke */ + template + 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 +class poly_vtable { + using inspector = typename Concept::template type; + + template + static auto vtable_entry(Ret(*)(inspector &, Args...)) -> Ret(*)(basic_any &, Args...); + + template + static auto vtable_entry(Ret(*)(const inspector &, Args...)) -> Ret(*)(const basic_any &, Args...); + + template + static auto vtable_entry(Ret(*)(Args...)) -> Ret(*)(const basic_any &, Args...); + + template + static auto vtable_entry(Ret(inspector:: *)(Args...)) -> Ret(*)(basic_any &, Args...); + + template + static auto vtable_entry(Ret(inspector:: *)(Args...) const) -> Ret(*)(const basic_any &, Args...); + + template + static auto make_vtable(value_list) + -> decltype(std::make_tuple(vtable_entry(Candidate)...)); + + template + [[nodiscard]] static constexpr auto make_vtable(type_list) { + if constexpr(sizeof...(Func) == 0) { + return decltype(make_vtable(typename Concept::template impl{})){}; + } else if constexpr((std::is_function_v && ...)) { + return decltype(std::make_tuple(vtable_entry(std::declval())...)){}; + } + } + + template + static void fill_vtable_entry(Ret(* &entry)(Any &, Args...)) { + if constexpr(std::is_invocable_r_v) { + entry = +[](Any &, Args... args) -> Ret { + return std::invoke(Candidate, std::forward(args)...); + }; + } else { + entry = +[](Any &instance, Args... args) -> Ret { + return static_cast(std::invoke(Candidate, any_cast &>(instance), std::forward(args)...)); + }; + } + } + + template + [[nodiscard]] static auto fill_vtable(std::index_sequence) { + type impl{}; + (fill_vtable_entry>>(std::get(impl)), ...); + return impl; + } + +public: + /*! @brief Virtual table type. */ + using type = decltype(make_vtable(Concept{})); + + /** + * @brief Returns a static virtual table for a specific concept and type. + * @tparam Type The type for which to generate the virtual table. + * @return A static virtual table for the given concept and type. + */ + template + [[nodiscard]] static const auto * instance() { + static_assert(std::is_same_v>, "Type differs from its decayed form"); + static const auto vtable = fill_vtable(std::make_index_sequence::size>{}); + return &vtable; + } +}; + + +/** + * @brief Poly base class used to inject functionalities into concepts. + * @tparam Poly The outermost poly class. + */ +template +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 + [[nodiscard]] decltype(auto) invoke(const poly_base &self, Args &&... args) const { + const auto &poly = static_cast(self); + return std::get(*poly.vtable)(poly.storage, std::forward(args)...); + } + + /*! @copydoc invoke */ + template + [[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&... args) { + auto &poly = static_cast(self); + return std::get(*poly.vtable)(poly.storage, std::forward(args)...); + } +}; + + +/** + * @brief Shortcut for calling `poly_base::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 +decltype(auto) poly_call(Poly &&self, Args &&... args) { + return std::forward(self).template invoke(self, std::forward(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.
+ * This class aims to make it simple and easy to use. + * + * @note + * Both deduced and defined static virtual tables are supported.
+ * 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 +class basic_poly: private Concept::template type>> { + /*! @brief A poly base is allowed to snoop into a poly object. */ + friend struct poly_base; + + using vtable_type = typename poly_vtable::type; + +public: + /*! @brief Concept type. */ + using concept_type = typename Concept::template 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 + explicit basic_poly(std::in_place_type_t, Args &&... args) + : storage{std::in_place_type, std::forward(args)...}, + vtable{poly_vtable::template instance>>()} + {} + + /** + * @brief Constructs a poly that holds an unmanaged object. + * @tparam Type Type of object to use to initialize the poly. + * @param value An instance of an object to use to initialize the poly. + */ + template + basic_poly(std::reference_wrapper value) + : basic_poly{std::in_place_type, value.get()} + {} + + /** + * @brief Constructs a poly from a given value. + * @tparam Type Type of object to use to initialize the poly. + * @param value An instance of an object to use to initialize the poly. + */ + template>, basic_poly>>> + basic_poly(Type &&value) ENTT_NOEXCEPT + : basic_poly{std::in_place_type>>, std::forward(value)} + {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + basic_poly(const basic_poly &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_poly(basic_poly &&other) ENTT_NOEXCEPT + : basic_poly{} + { + swap(*this, other); + } + + /** + * @brief Assignment operator. + * @param other The instance to assign from. + * @return This poly object. + */ + basic_poly & operator=(basic_poly other) { + swap(other, *this); + return *this; + } + + /** + * @brief Returns the type of the contained object. + * @return The type of the contained object, if any. + */ + [[nodiscard]] type_info type() const ENTT_NOEXCEPT { + return storage.type(); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void * data() const ENTT_NOEXCEPT { + return storage.data(); + } + + /*! @copydoc data */ + [[nodiscard]] void * data() ENTT_NOEXCEPT { + return storage.data(); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the poly. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + void emplace(Args &&... args) { + *this = basic_poly{std::in_place_type, std::forward(args)...}; + } + + /*! @brief Destroys contained object */ + void reset() { + *this = basic_poly{}; + } + + /** + * @brief Returns false if a poly is empty, true otherwise. + * @return False if the poly is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(vtable == nullptr); + } + + /** + * @brief Returns a pointer to the underlying concept. + * @return A pointer to the underlying concept. + */ + [[nodiscard]] concept_type * operator->() ENTT_NOEXCEPT { + return this; + } + + /*! @copydoc operator-> */ + [[nodiscard]] const concept_type * operator->() const ENTT_NOEXCEPT { + return this; + } + + /** + * @brief Swaps two poly objects. + * @param lhs A valid poly object. + * @param rhs A valid poly object. + */ + friend void swap(basic_poly &lhs, basic_poly &rhs) { + using std::swap; + swap(lhs.storage, rhs.storage); + swap(lhs.vtable, rhs.vtable); + } + + /** + * @brief Aliasing constructor. + * @return A poly that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_poly as_ref() ENTT_NOEXCEPT { + basic_poly ref = std::as_const(*this).as_ref(); + ref.storage = storage.as_ref(); + return ref; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_poly as_ref() const ENTT_NOEXCEPT { + basic_poly ref{}; + ref.storage = storage.as_ref(); + ref.vtable = vtable; + return ref; + } + +private: + basic_any storage; + const vtable_type *vtable; +}; + + +} + + +#endif + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Basic poly storage implementation. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +struct Storage: type_list { + /** + * @brief Concept definition. + * @tparam Base Opaque base class from which to inherit. + */ + template + struct type: Base { + /** + * @brief Returns a type info for the contained objects. + * @return The type info for the contained objects. + */ + type_info value_type() const ENTT_NOEXCEPT { + return poly_call<0>(*this); + } + }; + + /** + * @brief Concept implementation. + * @tparam Type Type for which to generate an implementation. + */ + template + using impl = value_list<&type_id>; +}; + + +/** + * @brief Defines the poly storage type associate with a given entity type. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +struct poly_storage_traits { + /*! @brief Poly storage type for the given entity type. */ + using storage_type = poly>; }; @@ -6284,16 +15577,13 @@ namespace entt { * @tparam Entity A valid entity type (see entt_traits for more details). */ template -class basic_runtime_view { - /*! @brief A registry is allowed to create views. */ - friend class basic_registry; - - using underlying_iterator = typename sparse_set::iterator; +class basic_runtime_view final { + using underlying_iterator = typename basic_sparse_set::iterator; class view_iterator final { friend class basic_runtime_view; - view_iterator(const std::vector *> &cpools, const std::vector *> &ignore, underlying_iterator curr) ENTT_NOEXCEPT + view_iterator(const std::vector *> &cpools, const std::vector *> &ignore, underlying_iterator curr) ENTT_NOEXCEPT : pools{&cpools}, filter{&ignore}, it{curr} @@ -6354,23 +15644,11 @@ class basic_runtime_view { } private: - const std::vector *> *pools; - const std::vector *> *filter; + const std::vector *> *pools; + const std::vector *> *filter; underlying_iterator it; }; - basic_runtime_view(std::vector *> cpools, std::vector *> epools) ENTT_NOEXCEPT - : pools{std::move(cpools)}, - filter{std::move(epools)} - { - const auto it = std::min_element(pools.begin(), pools.end(), [](const auto *lhs, const auto *rhs) { - return (!lhs && rhs) || (lhs && rhs && lhs->size() < rhs->size()); - }); - - // brings the best candidate (if any) on front of the vector - std::rotate(pools.begin(), it, pools.end()); - } - [[nodiscard]] bool valid() const { return !pools.empty() && pools.front(); } @@ -6383,20 +15661,35 @@ public: /*! @brief Bidirectional iterator type. */ using iterator = view_iterator; + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_runtime_view() ENTT_NOEXCEPT + : pools{}, + filter{} + {} + /** - * @brief Estimates the number of entities that have the given components. - * @return Estimated number of entities that have the given components. + * @brief Constructs a runtime view from a set of storage classes. + * @param cpools The storage for the types to iterate. + * @param epools The storage for the types used to filter the view. */ - [[nodiscard]] size_type size() const { - return valid() ? pools.front()->size() : size_type{}; + basic_runtime_view(std::vector *> cpools, std::vector *> epools) ENTT_NOEXCEPT + : pools{std::move(cpools)}, + filter{std::move(epools)} + { + const auto it = std::min_element(pools.begin(), pools.end(), [](const auto *lhs, const auto *rhs) { + return (!lhs && rhs) || (lhs && rhs && lhs->size() < rhs->size()); + }); + + // brings the best candidate (if any) on front of the vector + std::rotate(pools.begin(), it, pools.end()); } /** - * @brief Checks if the view is definitely empty. - * @return True if the view is definitely empty, false otherwise. + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. */ - [[nodiscard]] bool empty() const { - return !valid() || pools.front()->empty(); + [[nodiscard]] size_type size_hint() const { + return valid() ? pools.front()->size() : size_type{}; } /** @@ -6407,10 +15700,6 @@ public: * components. If the view is empty, the returned iterator will be equal to * `end()`. * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the first entity that has the given components. */ [[nodiscard]] iterator begin() const { @@ -6425,10 +15714,6 @@ public: * has the given components. Attempting to dereference the returned iterator * results in undefined behavior. * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the entity following the last entity that has the * given components. */ @@ -6469,8 +15754,8 @@ public: } private: - std::vector *> pools; - std::vector *> filter; + std::vector *> pools; + std::vector *> filter; }; @@ -6504,10 +15789,10 @@ private: // #include "fwd.hpp" -// #include "pool.hpp" - // #include "sparse_set.hpp" +// #include "storage.hpp" + // #include "utility.hpp" @@ -6559,24 +15844,17 @@ class basic_view; * @tparam Component Types of components iterated by the view. */ template -class basic_view, Component...> { - /*! @brief A registry is allowed to create views. */ - friend class basic_registry; - +class basic_view, Component...> final { template - using pool_type = pool_t; + using storage_type = constness_as_t>::storage_type, Comp>; - template - using component_iterator = decltype(std::declval>().begin()); - - using unchecked_type = std::array *, (sizeof...(Component) - 1)>; - using filter_type = std::array *, sizeof...(Exclude)>; + using unchecked_type = std::array *, (sizeof...(Component) - 1)>; template class view_iterator final { friend class basic_view, Component...>; - view_iterator(It from, It to, It curr, unchecked_type other, filter_type ignore) ENTT_NOEXCEPT + view_iterator(It from, It to, It curr, unchecked_type other, const std::tuple *...> &ignore) ENTT_NOEXCEPT : first{from}, last{to}, it{curr}, @@ -6589,8 +15867,10 @@ class basic_view, Component...> { } [[nodiscard]] bool valid() const { - return std::all_of(unchecked.cbegin(), unchecked.cend(), [entt = *it](const sparse_set *curr) { return curr->contains(entt); }) - && (sizeof...(Exclude) == 0 || std::none_of(filter.cbegin(), filter.cend(), [entt = *it](const sparse_set *cpool) { return cpool->contains(entt); })); + const auto entt = *it; + + return std::all_of(unchecked.cbegin(), unchecked.cend(), [entt](const basic_sparse_set *curr) { return curr->contains(entt); }) + && !(std::get *>(filter)->contains(entt) || ...); } public: @@ -6600,14 +15880,16 @@ class basic_view, Component...> { using reference = typename std::iterator_traits::reference; using iterator_category = std::bidirectional_iterator_tag; - view_iterator() ENTT_NOEXCEPT = default; + view_iterator() ENTT_NOEXCEPT + : view_iterator{{}, {}, {}, {}, {}} + {} - view_iterator & operator++() { + view_iterator & operator++() ENTT_NOEXCEPT { while(++it != last && !valid()); return *this; } - view_iterator operator++(int) { + view_iterator operator++(int) ENTT_NOEXCEPT { view_iterator orig = *this; return ++(*this), orig; } @@ -6643,182 +15925,134 @@ class basic_view, Component...> { It last; It it; unchecked_type unchecked; - filter_type filter; + std::tuple *...> filter; }; - class view_proxy { + class iterable_view final { friend class basic_view, Component...>; - using proxy_view_iterator = view_iterator::iterator>; + template + class iterable_view_iterator final { + friend class iterable_view; - class proxy_iterator { - friend class view_proxy; - - using ref_type = decltype(std::tuple_cat(std::declval, std::tuple<>, std::tuple *>>>()...)); - - proxy_iterator(proxy_view_iterator from, ref_type ref) ENTT_NOEXCEPT + iterable_view_iterator(It from, const basic_view *parent) ENTT_NOEXCEPT : it{from}, - pools{ref} + view{parent} {} public: using difference_type = std::ptrdiff_t; - using value_type = decltype(std::tuple_cat( - std::declval>(), - std::declval, std::tuple<>, std::tuple>>()... - )); + using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); using pointer = void; - using reference = decltype(std::tuple_cat( - std::declval>(), - std::declval, std::tuple<>, std::tuple>>()... - )); + using reference = value_type; using iterator_category = std::input_iterator_tag; - proxy_iterator & operator++() ENTT_NOEXCEPT { + iterable_view_iterator & operator++() ENTT_NOEXCEPT { return ++it, *this; } - proxy_iterator operator++(int) ENTT_NOEXCEPT { - proxy_iterator orig = *this; + iterable_view_iterator operator++(int) ENTT_NOEXCEPT { + iterable_view_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { - return std::apply([entt = *it](auto *... cpool) { return reference{entt, cpool->get(entt)...}; }, pools); + return std::tuple_cat(std::make_tuple(*it), view->get(*it)); } - [[nodiscard]] bool operator==(const proxy_iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator==(const iterable_view_iterator &other) const ENTT_NOEXCEPT { return other.it == it; } - [[nodiscard]] bool operator!=(const proxy_iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator!=(const iterable_view_iterator &other) const ENTT_NOEXCEPT { return !(*this == other); } private: - proxy_view_iterator it{}; - const ref_type pools{}; + It it; + const basic_view *view; }; - view_proxy(proxy_view_iterator from, proxy_view_iterator to, std::tuple *...> ref) - : first{from}, - last{to}, - pools{ref} + iterable_view(const basic_view &parent) + : view{parent} {} public: - using iterator = proxy_iterator; + using iterator = iterable_view_iterator::iterator>>; + using reverse_iterator = iterable_view_iterator::reverse_iterator>>; [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { - return proxy_iterator{first, std::tuple_cat([](auto *cpool) { - if constexpr(is_eto_eligible_v::object_type>) { - return std::make_tuple(); - } else { - return std::make_tuple(cpool); - } - }(std::get *>(pools))...)}; + return { view.begin(), &view }; } [[nodiscard]] iterator end() const ENTT_NOEXCEPT { - return proxy_iterator{last, std::tuple_cat([](auto *cpool) { - if constexpr(is_eto_eligible_v::object_type>) { - return std::make_tuple(); - } else { - return std::make_tuple(cpool); - } - }(std::get *>(pools))...)}; + return { view.end(), &view }; + } + + [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return { view.rbegin(), &view }; + } + + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return { view.rend(), &view }; } private: - proxy_view_iterator first; - proxy_view_iterator last; - const std::tuple *...> pools; + const basic_view view; }; - basic_view(pool_type &... component, unpack_as_t, Exclude> &... epool) ENTT_NOEXCEPT - : pools{&component...}, - view{candidate()}, - filter{&epool...} - {} - - [[nodiscard]] const sparse_set * candidate() const ENTT_NOEXCEPT { - return (std::min)({ static_cast *>(std::get *>(pools))... }, [](const auto *lhs, const auto *rhs) { + [[nodiscard]] const basic_sparse_set * candidate() const ENTT_NOEXCEPT { + return (std::min)({ static_cast *>(std::get *>(pools))... }, [](const auto *lhs, const auto *rhs) { return lhs->size() < rhs->size(); }); } - [[nodiscard]] unchecked_type unchecked(const sparse_set *cpool) const { + [[nodiscard]] unchecked_type unchecked(const basic_sparse_set *cpool) const { std::size_t pos{}; unchecked_type other{}; - ((std::get *>(pools) == cpool ? nullptr : (other[pos] = std::get *>(pools), other[pos++])), ...); + (static_cast(std::get *>(pools) == cpool ? void() : void(other[pos++] = std::get *>(pools))), ...); return other; } - template - [[nodiscard]] decltype(auto) get([[maybe_unused]] component_iterator &it, [[maybe_unused]] pool_type *cpool, [[maybe_unused]] const Entity entt) const { - if constexpr(std::is_same_v) { - return *it; + template + [[nodiscard]] auto dispatch_get([[maybe_unused]] It &it, [[maybe_unused]] const Entity entt) const { + if constexpr(std::is_same_v::value_type, typename storage_type::value_type>) { + return std::forward_as_tuple(*it); } else { - return cpool->get(entt); + return get_as_tuple(*std::get *>(pools), entt); } } - template - void traverse(Func func, type_list) const { - if constexpr(std::disjunction_v...>) { - auto it = std::get *>(pools)->begin(); - - for(const auto entt: static_cast &>(*std::get *>(pools))) { - if(((std::is_same_v || std::get *>(pools)->contains(entt)) && ...) - && (sizeof...(Exclude) == 0 || std::none_of(filter.cbegin(), filter.cend(), [entt](const sparse_set *cpool) { return cpool->contains(entt); }))) + template + void traverse(Func func) const { + if constexpr(std::is_void_v *>(pools)->get({}))>) { + for(const auto entt: static_cast &>(*std::get *>(pools))) { + if(((std::is_same_v || std::get *>(pools)->contains(entt)) && ...) + && !(std::get *>(filter)->contains(entt) || ...)) { - if constexpr(std::is_invocable_v({}))...>) { - func(get(it, std::get *>(pools), entt)...); + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); } else { - func(entt, get(it, std::get *>(pools), entt)...); + std::apply(func, get(entt)); + } + } + } + } else { + auto it = std::get *>(pools)->begin(); + + for(const auto entt: static_cast &>(*std::get *>(pools))) { + if(((std::is_same_v || std::get *>(pools)->contains(entt)) && ...) + && !(std::get *>(filter)->contains(entt) || ...)) + { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(it, entt)...)); + } else { + std::apply(func, std::tuple_cat(dispatch_get(it, entt)...)); } } ++it; } - } else { - for(const auto entt: static_cast &>(*std::get *>(pools))) { - if(((std::is_same_v || std::get *>(pools)->contains(entt)) && ...) - && (sizeof...(Exclude) == 0 || std::none_of(filter.cbegin(), filter.cend(), [entt](const sparse_set *cpool) { return cpool->contains(entt); }))) - { - if constexpr(std::is_invocable_v({}))...>) { - func(std::get *>(pools)->get(entt)...); - } else { - func(entt, std::get *>(pools)->get(entt)...); - } - } - } - } - } - - template - void iterate(Func func, type_list) const { - const auto last = view->data() + view->size(); - auto first = view->data(); - - while(first != last) { - if((std::get *>(pools)->contains(*first) && ...) - && (sizeof...(Exclude) == 0 || std::none_of(filter.cbegin(), filter.cend(), [entt = *first](const sparse_set *cpool) { return cpool->contains(entt); }))) - { - const auto base = *(first++); - const auto chunk = (std::min)({ (std::get *>(pools)->size() - std::get *>(pools)->index(base))... }); - size_type length{}; - - for(++length; - length < chunk - && ((*(std::get *>(pools)->data() + std::get *>(pools)->index(base) + length) == *first) && ...) - && (sizeof...(Exclude) == 0 || std::none_of(filter.cbegin(), filter.cend(), [entt = *first](const sparse_set *cpool) { return cpool->contains(entt); })); - ++length, ++first); - - func(view->data() + view->index(base), (std::get *>(pools)->raw() + std::get *>(pools)->index(base))..., length); - } else { - ++first; - } } } @@ -6828,86 +16062,41 @@ public: /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Bidirectional iterator type. */ - using iterator = view_iterator::iterator>; + using iterator = view_iterator::iterator>; /*! @brief Reverse iterator type. */ - using reverse_iterator = view_iterator::reverse_iterator>; + using reverse_iterator = view_iterator::reverse_iterator>; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() ENTT_NOEXCEPT + : view{} + {} /** - * @brief Returns the number of existing components of the given type. - * - * This isn't the number of entities iterated by the view. - * - * @tparam Comp Type of component of which to return the size. - * @return Number of existing components of the given type. + * @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, const storage_type &... epool) ENTT_NOEXCEPT + : pools{&component...}, + filter{&epool...}, + view{candidate()} + {} + + /** + * @brief Forces the type to use to drive iterations. + * @tparam Comp Type of component to use to drive the iteration. */ template - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { - return std::get *>(pools)->size(); + void use() const ENTT_NOEXCEPT { + view = std::get *>(pools); } /** * @brief Estimates the number of entities iterated by the view. * @return Estimated number of entities iterated by the view. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { - return (std::min)({ std::get *>(pools)->size()... }); - } - - /** - * @brief Checks whether a view or some pools are empty. - * - * The view is definitely empty if one of the pools it uses is empty. In all - * other cases, the view may be empty and not return entities even if this - * function returns false. - * - * @tparam Comp Types of components in which one is interested. - * @return True if the view or the pools are empty, false otherwise. - */ - template - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { - if constexpr(sizeof...(Comp) == 0) { - return (std::get *>(pools)->empty() || ...); - } else { - return (std::get *>(pools)->empty() && ...); - } - } - - /** - * @brief Direct access to the list of components of a given pool. - * - * The returned pointer is such that range - * `[raw(), raw() + size()]` is always a valid range, even - * if the container is empty. - * - * @note - * Components are in the reverse order as returned by the `begin`/`end` - * iterators. - * - * @tparam Comp Type of component in which one is interested. - * @return A pointer to the array of components. - */ - template - [[nodiscard]] Comp * raw() const ENTT_NOEXCEPT { - return std::get *>(pools)->raw(); - } - - /** - * @brief Direct access to the list of entities of a given pool. - * - * 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. - * - * @tparam Comp Type of component in which one is interested. - * @return A pointer to the array of entities. - */ - template - [[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT { - return std::get *>(pools)->data(); + [[nodiscard]] size_type size_hint() const ENTT_NOEXCEPT { + return view->size(); } /** @@ -6916,10 +16105,6 @@ public: * The returned iterator points to the first entity of the view. If the view * is empty, the returned iterator will be equal to `end()`. * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the first entity of the view. */ [[nodiscard]] iterator begin() const { @@ -6933,10 +16118,6 @@ public: * the view. Attempting to dereference the returned iterator results in * undefined behavior. * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the entity following the last entity of the view. */ [[nodiscard]] iterator end() const { @@ -6949,10 +16130,6 @@ public: * 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()`. * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the first entity of the reversed view. */ [[nodiscard]] reverse_iterator rbegin() const { @@ -6967,10 +16144,6 @@ public: * the reversed view. Attempting to dereference the returned iterator * results in undefined behavior. * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the entity following the last entity of the * reversed view. */ @@ -7005,18 +16178,25 @@ public: * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const { - iterator it{view->begin(), view->end(), view->find(entt), unchecked(view), filter}; + const auto it = iterator{view->begin(), view->end(), view->find(entt), unchecked(view), filter}; return (it != end() && *it == entt) ? it : end(); } + /** + * @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 entity identifier. * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const { - return (std::get *>(pools)->contains(entt) && ...) - && (sizeof...(Exclude) == 0 || std::none_of(filter.begin(), filter.end(), [entt](const auto *cpool) { return cpool->contains(entt); })); + return (std::get *>(pools)->contains(entt) && ...) && !(std::get *>(filter)->contains(entt) || ...); } /** @@ -7028,9 +16208,7 @@ public: * @warning * Attempting to use an invalid component type results in a compilation * error. Attempting to use an entity that doesn't belong to the view - * results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * view doesn't contain the given entity. + * results in undefined behavior. * * @tparam Comp Types of components to get. * @param entt A valid entity identifier. @@ -7041,12 +16219,11 @@ public: ENTT_ASSERT(contains(entt)); if constexpr(sizeof...(Comp) == 0) { - static_assert(sizeof...(Component) == 1, "Invalid component type"); - return (std::get *>(pools)->get(entt), ...); + return std::tuple_cat(get_as_tuple(*std::get *>(pools), entt)...); } else if constexpr(sizeof...(Comp) == 1) { - return (std::get *>(pools)->get(entt), ...); + return (std::get *>(pools)->get(entt), ...); } else { - return std::tuple({}))...>{get(entt)...}; + return std::tuple_cat(get_as_tuple(*std::get *>(pools), entt)...); } } @@ -7074,8 +16251,7 @@ public: */ template void each(Func func) const { - view = candidate(); - ((std::get *>(pools) == view ? each(std::move(func)) : void()), ...); + ((std::get *>(pools) == view ? traverse(std::move(func)) : void()), ...); } /** @@ -7090,14 +16266,14 @@ public: * * @sa each * - * @tparam Comp Type of component to use to enforce the iteration order. + * @tparam Comp Type of component to use to drive the iteration. * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { - using non_empty_type = type_list_cat_t, type_list<>, type_list>...>; - traverse(std::move(func), non_empty_type{}); + use(); + traverse(std::move(func)); } /** @@ -7113,9 +16289,8 @@ public: * * @return An iterable object to use to _visit_ the view. */ - [[nodiscard]] auto proxy() const ENTT_NOEXCEPT { - view = candidate(); - return view_proxy{begin(), end(), pools}; + [[nodiscard]] iterable_view each() const ENTT_NOEXCEPT { + return iterable_view{*this}; } /** @@ -7129,60 +16304,31 @@ public: * * @sa each * - * @tparam Comp Type of component to use to enforce the iteration order. + * @tparam Comp Type of component to use to drive the iteration. * @return An iterable object to use to _visit_ the view. */ template - [[nodiscard]] auto proxy() const ENTT_NOEXCEPT { - const sparse_set *cpool = std::get *>(pools); - iterator first{cpool->begin(), cpool->end(), cpool->begin(), unchecked(cpool), filter}; - iterator last{cpool->begin(), cpool->end(), cpool->end(), unchecked(cpool), filter}; - return view_proxy{std::move(first), std::move(last), pools}; + [[nodiscard]] iterable_view each() const ENTT_NOEXCEPT { + use(); + return iterable_view{*this}; } /** - * @brief Chunked iteration for entities and components - * - * Chunked iteration tries to spot chunks in the sets of entities and - * components and return them one at a time along with their sizes.
- * This type of iteration is intended where it's known a priori that the - * creation of entities and components takes place in chunk, which is - * actually quite common. In this case, various optimizations can be applied - * downstream to obtain even better performances from the views. - * - * The signature of the function must be equivalent to the following: - * - * @code{.cpp} - * void(const entity_type *, Type *..., size_type); - * @endcode - * - * The arguments are as follows: - * - * * A pointer to the entities belonging to the chunk. - * * Pointers to the components associated with the returned entities. - * * The length of the chunk. - * - * Note that the callback can be invoked 0 or more times and no guarantee is - * given on the order of the elements. - * - * @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. + * @brief Combines two views in a _more specific_ one (friend function). + * @tparam Id A valid entity type (see entt_traits for more details). + * @tparam ELhs Filter list of the first view. + * @tparam CLhs Component list of the first view. + * @tparam ERhs Filter list of the second view. + * @tparam CRhs Component list of the second view. + * @return A more specific view. */ - template - void chunked(Func func) const { - using non_empty_type = type_list_cat_t, type_list<>, type_list>...>; - view = candidate(); - iterate(std::move(func), non_empty_type{}); - } + template + friend auto operator|(const basic_view, CLhs...> &, const basic_view, CRhs...> &); private: - const std::tuple *...> pools; - mutable const sparse_set* view; - filter_type filter; + const std::tuple *...> pools; + const std::tuple *...> filter; + mutable const basic_sparse_set *view; }; @@ -7218,91 +16364,89 @@ private: * @tparam Component Type of component iterated by the view. */ template -class basic_view, Component> { - /*! @brief A registry is allowed to create views. */ - friend class basic_registry; +class basic_view, Component> final { + using storage_type = constness_as_t>::storage_type, Component>; - using pool_type = pool_t; - - class view_proxy { + class iterable_view { friend class basic_view, Component>; - class proxy_iterator { - friend class view_proxy; + template + class iterable_view_iterator { + friend class iterable_view; - using it_type = std::conditional_t< - is_eto_eligible_v, - std::tuple::iterator>, - std::tuple::iterator, decltype(std::declval().begin())> - >; - - proxy_iterator(it_type from) ENTT_NOEXCEPT - : it{from} + template + iterable_view_iterator(It... from, Discard...) ENTT_NOEXCEPT + : it{from...} {} public: using difference_type = std::ptrdiff_t; - using value_type = std::conditional_t, std::tuple, std::tuple>; + using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); using pointer = void; - using reference = std::conditional_t, std::tuple, std::tuple>; + using reference = value_type; using iterator_category = std::input_iterator_tag; - proxy_iterator & operator++() ENTT_NOEXCEPT { - return std::apply([](auto &&... curr) { (++curr, ...); }, it), *this; + iterable_view_iterator & operator++() ENTT_NOEXCEPT { + return (++std::get(it), ...), *this; } - proxy_iterator operator++(int) ENTT_NOEXCEPT { - proxy_iterator orig = *this; + iterable_view_iterator operator++(int) ENTT_NOEXCEPT { + iterable_view_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { - return std::apply([](auto &&... curr) { return reference{*curr...}; }, it); + return { *std::get(it)... }; } - [[nodiscard]] bool operator==(const proxy_iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator==(const iterable_view_iterator &other) const ENTT_NOEXCEPT { return std::get<0>(other.it) == std::get<0>(it); } - [[nodiscard]] bool operator!=(const proxy_iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator!=(const iterable_view_iterator &other) const ENTT_NOEXCEPT { return !(*this == other); } private: - it_type it{}; + std::tuple it; }; - view_proxy(pool_type &ref) + iterable_view(storage_type &ref) : pool{&ref} {} public: - using iterator = proxy_iterator; + using iterator = std::conditional_t< + std::is_void_v().get({}))>, + iterable_view_iterator::iterator>, + iterable_view_iterator::iterator, decltype(std::declval().begin())> + >; + using reverse_iterator = std::conditional_t< + std::is_void_v().get({}))>, + iterable_view_iterator::reverse_iterator>, + iterable_view_iterator::reverse_iterator, decltype(std::declval().rbegin())> + >; [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { - if constexpr(is_eto_eligible_v) { - return proxy_iterator{std::make_tuple(pool->sparse_set::begin())}; - } else { - return proxy_iterator{std::make_tuple(pool->sparse_set::begin(), pool->begin())}; - } + return iterator{pool->basic_sparse_set::begin(), pool->begin()}; } [[nodiscard]] iterator end() const ENTT_NOEXCEPT { - if constexpr(is_eto_eligible_v) { - return proxy_iterator{std::make_tuple(pool->sparse_set::end())}; - } else { - return proxy_iterator{std::make_tuple(pool->sparse_set::end(), pool->end())}; - } + return iterator{pool->basic_sparse_set::end(), pool->end()}; + } + + [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return reverse_iterator{pool->basic_sparse_set::rbegin(), pool->rbegin()}; + } + + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return reverse_iterator{pool->basic_sparse_set::rend(), pool->rend()}; } private: - pool_type *pool; + storage_type * const pool; }; - basic_view(pool_type &ref) ENTT_NOEXCEPT - : pool{&ref} - {} - public: /*! @brief Type of component iterated by the view. */ using raw_type = Component; @@ -7311,16 +16455,31 @@ public: /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Random access iterator type. */ - using iterator = typename sparse_set::iterator; + using iterator = typename basic_sparse_set::iterator; /*! @brief Reversed iterator type. */ - using reverse_iterator = typename sparse_set::reverse_iterator; + using reverse_iterator = typename basic_sparse_set::reverse_iterator; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() ENTT_NOEXCEPT + : pools{}, + filter{} + {} + + /** + * @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{} + {} /** * @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 pool->size(); + return std::get<0>(pools)->size(); } /** @@ -7328,39 +16487,31 @@ public: * @return True if the view is empty, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { - return pool->empty(); + return std::get<0>(pools)->empty(); } /** * @brief Direct access to the list of components. * - * The returned pointer is such that range `[raw(), raw() + size()]` is + * The returned pointer is such that range `[raw(), raw() + size())` is * always a valid range, even if the container is empty. * - * @note - * Components are in the reverse order as returned by the `begin`/`end` - * iterators. - * * @return A pointer to the array of components. */ [[nodiscard]] raw_type * raw() const ENTT_NOEXCEPT { - return pool->raw(); + return std::get<0>(pools)->raw(); } /** * @brief Direct access to the list of entities. * - * The returned pointer is such that range `[data(), data() + size()]` is + * 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 pool->data(); + return std::get<0>(pools)->data(); } /** @@ -7369,14 +16520,10 @@ public: * The returned iterator points to the first entity of the view. If the view * is empty, the returned iterator will be equal to `end()`. * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the first entity of the view. */ [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { - return pool->sparse_set::begin(); + return std::get<0>(pools)->basic_sparse_set::begin(); } /** @@ -7386,14 +16533,10 @@ public: * the view. Attempting to dereference the returned iterator results in * undefined behavior. * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the entity following the last entity of the view. */ [[nodiscard]] iterator end() const ENTT_NOEXCEPT { - return pool->sparse_set::end(); + return std::get<0>(pools)->basic_sparse_set::end(); } /** @@ -7402,14 +16545,10 @@ public: * 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()`. * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the first entity of the reversed view. */ [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { - return pool->sparse_set::rbegin(); + return std::get<0>(pools)->basic_sparse_set::rbegin(); } /** @@ -7420,15 +16559,11 @@ public: * the reversed view. Attempting to dereference the returned iterator * results in undefined behavior. * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the entity following the last entity of the * reversed view. */ [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { - return pool->sparse_set::rend(); + return std::get<0>(pools)->basic_sparse_set::rend(); } /** @@ -7458,7 +16593,7 @@ public: * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const { - const auto it = pool->find(entt); + const auto it = std::get<0>(pools)->find(entt); return it != end() && *it == entt ? it : end(); } @@ -7471,13 +16606,21 @@ public: return begin()[pos]; } + /** + * @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 std::get<0>(pools) != nullptr; + } + /** * @brief Checks if a view contains an entity. * @param entt A valid entity identifier. * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const { - return pool->contains(entt); + return std::get<0>(pools)->contains(entt); } /** @@ -7487,19 +16630,24 @@ public: * far better performance than its counterpart. * * @warning - * Attempting to use an entity that doesn't belong to the view results in - * undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * view doesn't contain the given entity. + * Attempting to use an invalid component type results in a compilation + * error. 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 entity identifier. * @return The component assigned to the entity. */ - template + template [[nodiscard]] decltype(auto) get(const entity_type entt) const { - static_assert(std::is_same_v, "Invalid component type"); ENTT_ASSERT(contains(entt)); - return pool->get(entt); + + if constexpr(sizeof...(Comp) == 0) { + return get_as_tuple(*std::get<0>(pools), entt); + } else { + static_assert(std::is_same_v, "Invalid component type"); + return std::get<0>(pools)->get(entt); + } } /** @@ -7526,26 +16674,24 @@ public: */ template void each(Func func) const { - if constexpr(is_eto_eligible_v) { + if constexpr(std::is_void_v(pools)->get({}))>) { if constexpr(std::is_invocable_v) { - for(auto pos = pool->size(); pos; --pos) { + for(auto pos = size(); pos; --pos) { func(); } } else { - for(const auto entt: *this) { - func(entt); + for(auto entity: *this) { + func(entity); } } } else { - if constexpr(std::is_invocable_v) { - for(auto &&component: *pool) { - func(component); + if constexpr(is_applicable_v) { + for(const auto pack: each()) { + std::apply(func, pack); } } else { - auto raw = pool->begin(); - - for(const auto entt: *this) { - func(entt, *(raw++)); + for(auto &&component: *std::get<0>(pools)) { + func(component); } } } @@ -7564,15 +16710,56 @@ public: * * @return An iterable object to use to _visit_ the view. */ - [[nodiscard]] auto proxy() const ENTT_NOEXCEPT { - return view_proxy{*pool}; + [[nodiscard]] iterable_view each() const ENTT_NOEXCEPT { + return iterable_view{*std::get<0>(pools)}; } + /** + * @brief Combines two views in a _more specific_ one (friend function). + * @tparam Id A valid entity type (see entt_traits for more details). + * @tparam ELhs Filter list of the first view. + * @tparam CLhs Component list of the first view. + * @tparam ERhs Filter list of the second view. + * @tparam CRhs Component list of the second view. + * @return A more specific view. + */ + template + friend auto operator|(const basic_view, CLhs...> &, const basic_view, CRhs...> &); + private: - pool_type *pool; + const std::tuple pools; + const std::tuple<> filter; }; +/** + * @brief Deduction guide. + * @tparam Storage Type of storage classes used to create the view. + * @param storage The storage for the types to iterate. + */ +template +basic_view(Storage &... storage) +-> basic_view, entt::exclude_t<>, constness_as_t...>; + + +/** + * @brief Combines two views in a _more specific_ one. + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam ELhs Filter list of the first view. + * @tparam CLhs Component list of the first view. + * @tparam ERhs Filter list of the second view. + * @tparam CRhs Component list of the second view. + * @param lhs A valid reference to the first view. + * @param rhs A valid reference to the second view. + * @return A more specific view. + */ +template +[[nodiscard]] auto operator|(const basic_view, CLhs...> &lhs, const basic_view, CRhs...> &rhs) { + using view_type = basic_view, CLhs..., CRhs...>; + return std::apply([](auto *... storage) { return view_type{*storage...}; }, std::tuple_cat(lhs.pools, rhs.pools, lhs.filter, rhs.filter)); +} + + } @@ -7596,85 +16783,14 @@ namespace entt { template class basic_registry { using traits_type = entt_traits; + using poly_storage_type = typename poly_storage_traits::storage_type; template - struct pool_handler final: storage { - static_assert(std::is_same_v>, "Invalid component type"); - - [[nodiscard]] auto on_construct() ENTT_NOEXCEPT { - return sink{construction}; - } - - [[nodiscard]] auto on_update() ENTT_NOEXCEPT { - return sink{update}; - } - - [[nodiscard]] auto on_destroy() ENTT_NOEXCEPT { - return sink{destruction}; - } - - template - decltype(auto) emplace(basic_registry &owner, const Entity entt, Args &&... args) { - storage::emplace(entt, std::forward(args)...); - construction.publish(owner, entt); - - if constexpr(!is_eto_eligible_v) { - return this->get(entt); - } - } - - template - void insert(basic_registry &owner, It first, It last, Args &&... args) { - storage::insert(first, last, std::forward(args)...); - - if(!construction.empty()) { - while(first != last) { construction.publish(owner, *(first++)); } - } - } - - void remove(basic_registry &owner, const Entity entt) { - destruction.publish(owner, entt); - this->erase(entt); - } - - template - void remove(basic_registry &owner, It first, It last) { - if(std::distance(first, last) == std::distance(this->begin(), this->end())) { - if(!destruction.empty()) { - while(first != last) { destruction.publish(owner, *(first++)); } - } - - this->clear(); - } else { - while(first != last) { this->remove(owner, *(first++)); } - } - } - - template - decltype(auto) patch(basic_registry &owner, const Entity entt, [[maybe_unused]] Func &&... func) { - if constexpr(is_eto_eligible_v) { - update.publish(owner, entt); - } else { - (std::forward(func)(this->get(entt)), ...); - update.publish(owner, entt); - return this->get(entt); - } - } - - decltype(auto) replace(basic_registry &owner, const Entity entt, Component component) { - return patch(owner, entt, [&component](auto &&curr) { curr = std::move(component); }); - } - - private: - sigh construction{}; - sigh destruction{}; - sigh update{}; - }; + using storage_type = constness_as_t>::storage_type, Component>; struct pool_data { - id_type type_id{}; - std::unique_ptr> pool{}; - void(* remove)(sparse_set &, basic_registry &, const Entity){}; + poly_storage_type poly; + std::unique_ptr> pool{}; }; template @@ -7682,25 +16798,25 @@ class basic_registry { template struct group_handler, get_t, Owned...> { - static_assert(std::conjunction_v>..., std::is_same>..., std::is_same>...>, "One or more component types are invalid"); - std::conditional_t, std::size_t> current{}; + static_assert(std::conjunction_v>..., std::is_same>..., std::is_same>...>, "One or more component types are invalid"); + std::conditional_t, std::size_t> current{}; template void maybe_valid_if(basic_registry &owner, const Entity entt) { - [[maybe_unused]] const auto cpools = std::forward_as_tuple(owner.assure()...); + [[maybe_unused]] const auto cpools = std::make_tuple(owner.assure()...); - const auto is_valid = ((std::is_same_v || std::get &>(cpools).contains(entt)) && ...) - && ((std::is_same_v || owner.assure().contains(entt)) && ...) - && ((std::is_same_v || !owner.assure().contains(entt)) && ...); + const auto is_valid = ((std::is_same_v || std::get *>(cpools)->contains(entt)) && ...) + && ((std::is_same_v || owner.assure()->contains(entt)) && ...) + && ((std::is_same_v || !owner.assure()->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)) { + if(is_valid && !(std::get<0>(cpools)->index(entt) < current)) { const auto pos = current++; - (std::get &>(cpools).swap(std::get &>(cpools).data()[pos], entt), ...); + (std::get *>(cpools)->swap(std::get *>(cpools)->data()[pos], entt), ...); } } } @@ -7708,12 +16824,12 @@ class basic_registry { void discard_if([[maybe_unused]] basic_registry &owner, const Entity entt) { if constexpr(sizeof...(Owned) == 0) { if(current.contains(entt)) { - current.erase(entt); + current.remove(entt); } } else { - if(const auto cpools = std::forward_as_tuple(owner.assure()...); std::get<0>(cpools).contains(entt) && (std::get<0>(cpools).index(entt) < current)) { + if(const auto cpools = std::make_tuple(owner.assure()...); std::get<0>(cpools)->contains(entt) && (std::get<0>(cpools)->index(entt) < current)) { const auto pos = --current; - (std::get &>(cpools).swap(std::get &>(cpools).data()[pos], entt), ...); + (std::get *>(cpools)->swap(std::get *>(cpools)->data()[pos], entt), ...); } } } @@ -7727,51 +16843,48 @@ class basic_registry { bool (* exclude)(const id_type) ENTT_NOEXCEPT; }; - struct variable_data { - id_type type_id; - std::unique_ptr value; - }; - template - [[nodiscard]] const pool_handler & assure() const { - const sparse_set *cpool; + [[nodiscard]] storage_type * assure() const { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); + const auto index = type_seq::value(); - if constexpr(ENTT_FAST_PATH(has_type_index_v)) { - const auto index = type_index::value(); - - if(!(index < pools.size())) { - pools.resize(size_type(index+1u)); - } - - if(auto &&pdata = pools[index]; !pdata.pool) { - pdata.type_id = type_info::id(); - pdata.pool.reset(new pool_handler()); - pdata.remove = [](sparse_set &target, basic_registry &owner, const entity_type entt) { - static_cast &>(target).remove(owner, entt); - }; - } - - cpool = pools[index].pool.get(); - } else { - if(const auto it = std::find_if(pools.cbegin(), pools.cend(), [id = type_info::id()](const auto &pdata) { return id == pdata.type_id; }); it == pools.cend()) { - cpool = pools.emplace_back(pool_data{ - type_info::id(), - std::unique_ptr>{new pool_handler()}, - [](sparse_set &target, basic_registry &owner, const entity_type entt) { - static_cast &>(target).remove(owner, entt); - } - }).pool.get(); - } else { - cpool = it->pool.get(); - } + if(!(index < pools.size())) { + pools.resize(size_type(index)+1u); } - return *static_cast *>(cpool); + if(auto &&pdata = pools[index]; !pdata.pool) { + pdata.pool.reset(new storage_type()); + pdata.poly.template emplace &>(*static_cast *>(pdata.pool.get())); + } + + return static_cast *>(pools[index].pool.get()); } template - [[nodiscard]] pool_handler & assure() { - return const_cast &>(std::as_const(*this).template assure()); + [[nodiscard]] const storage_type * pool_if_exists() const { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); + const auto index = type_seq::value(); + return (!(index < pools.size()) || !pools[index].pool) ? nullptr : static_cast *>(pools[index].pool.get()); + } + + Entity generate_identifier() { + // traits_type::entity_mask is reserved to allow for null identifiers + ENTT_ASSERT(static_cast(entities.size()) < traits_type::entity_mask); + return entities.emplace_back(entity_type{static_cast(entities.size())}); + } + + Entity recycle_identifier() { + ENTT_ASSERT(available != null); + const auto curr = to_integral(available); + const auto version = to_integral(entities[curr]) & (traits_type::version_mask << traits_type::entity_shift); + available = entity_type{to_integral(entities[curr]) & traits_type::entity_mask}; + return entities[curr] = entity_type{curr | version}; + } + + void release_entity(const Entity entity, const typename traits_type::version_type version) { + const auto entt = to_integral(entity) & traits_type::entity_mask; + entities[entt] = entity_type{to_integral(available) | (typename traits_type::entity_type{version} << traits_type::entity_shift)}; + available = entity_type{entt}; } public: @@ -7781,6 +16894,26 @@ public: using version_type = typename traits_type::version_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; + /*! @brief Poly storage type. */ + using poly_storage = typename poly_storage_traits::storage_type; + + /** + * @brief Returns the entity identifier without the version. + * @param entity An entity identifier, either valid or not. + * @return The entity identifier without the version. + */ + [[nodiscard]] static entity_type entity(const entity_type entity) ENTT_NOEXCEPT { + return entity_type{to_integral(entity) & traits_type::entity_mask}; + } + + /** + * @brief Returns the version stored along with an entity identifier. + * @param entity An entity identifier, either valid or not. + * @return The version stored along with the given entity identifier. + */ + [[nodiscard]] static version_type version(const entity_type entity) ENTT_NOEXCEPT { + return version_type(to_integral(entity) >> traits_type::entity_shift); + } /*! @brief Default constructor. */ basic_registry() = default; @@ -7801,6 +16934,23 @@ public: static_cast(assure()); } + /** + * @brief Returns a poly storage for a given type. + * @param info The type for which to return a poly storage. + * @return A valid poly storage if a pool for the given type exists, an + * empty and thus invalid element otherwise. + */ + poly_storage & storage(const type_info info) { + ENTT_ASSERT(info.seq() < pools.size() && pools[info.seq()].poly); + return pools[info.seq()].poly; + } + + /*! @copydoc storage */ + const poly_storage & storage(const type_info info) const { + ENTT_ASSERT(info.seq() < pools.size() && pools[info.seq()].poly); + return pools[info.seq()].poly; + } + /** * @brief Returns the number of existing components of the given type. * @tparam Component Type of component of which to return the size. @@ -7808,7 +16958,8 @@ public: */ template [[nodiscard]] size_type size() const { - return assure().size(); + const auto *cpool = pool_if_exists(); + return cpool ? cpool->size() : size_type{}; } /** @@ -7825,9 +16976,8 @@ public: */ [[nodiscard]] size_type alive() const { auto sz = entities.size(); - auto curr = destroyed; - for(; curr != null; --sz) { + for(auto curr = available; curr != null; --sz) { curr = entities[to_integral(curr) & traits_type::entity_mask]; } @@ -7852,10 +17002,18 @@ public: if constexpr(sizeof...(Component) == 0) { entities.reserve(cap); } else { - (assure().reserve(cap), ...); + (assure()->reserve(cap), ...); } } + /** + * @brief Reserves enough space to store `count` pools. + * @param count Number of pools to reserve space for. + */ + void reserve_pools(const size_t count) { + pools.reserve(count); + } + /** * @brief Returns the capacity of the pool for the given component. * @tparam Component Type of component in which one is interested. @@ -7863,7 +17021,8 @@ public: */ template [[nodiscard]] size_type capacity() const { - return assure().capacity(); + const auto *cpool = pool_if_exists(); + return cpool ? cpool->capacity() : size_type{}; } /** @@ -7882,7 +17041,7 @@ public: */ template void shrink_to_fit() { - (assure().shrink_to_fit(), ...); + (assure()->shrink_to_fit(), ...); } /** @@ -7901,60 +17060,14 @@ public: if constexpr(sizeof...(Component) == 0) { return !alive(); } else { - return (assure().empty() && ...); + return [](const auto *... cpool) { return ((!cpool || cpool->empty()) && ...); }(pool_if_exists()...); } } - /** - * @brief Direct access to the list of components of a given pool. - * - * The returned pointer is such that range - * `[raw(), raw() + size()]` is always a - * valid range, even if the container is empty. - * - * Components are in the reverse order as imposed by the sorting - * functionalities. - * - * @note - * Empty components aren't explicitly instantiated. Therefore, this function - * isn't available for them. A compilation error will occur if invoked. - * - * @tparam Component Type of component in which one is interested. - * @return A pointer to the array of components of the given type. - */ - template - [[nodiscard]] const Component * raw() const { - return assure().raw(); - } - - /*! @copydoc raw */ - template - [[nodiscard]] Component * raw() { - return const_cast(std::as_const(*this).template raw()); - } - - /** - * @brief Direct access to the list of entities of a given pool. - * - * The returned pointer is such that range - * `[data(), data() + size()]` is always a - * valid range, even if the container is empty. - * - * Entities are in the reverse order as imposed by the sorting - * functionalities. - * - * @tparam Component Type of component in which one is interested. - * @return A pointer to the array of entities. - */ - template - [[nodiscard]] const entity_type * data() const { - return assure().data(); - } - /** * @brief Direct access to the list of entities of a registry. * - * The returned pointer is such that range `[data(), data() + size()]` is + * The returned pointer is such that range `[data(), data() + size())` is * always a valid range, even if the container is empty. * * @warning @@ -7967,6 +17080,18 @@ public: return entities.data(); } + /** + * @brief Returns the head of the list of destroyed entities. + * + * This function is intended for use in conjunction with `assign`.
+ * The returned entity has an invalid identifier in all cases. + * + * @return The head of the list of destroyed entities. + */ + [[nodiscard]] entity_type destroyed() const ENTT_NOEXCEPT { + return available; + } + /** * @brief Checks if an entity identifier refers to a valid entity. * @param entity An entity identifier, either valid or not. @@ -7977,33 +17102,13 @@ public: return (pos < entities.size() && entities[pos] == entity); } - /** - * @brief Returns the entity identifier without the version. - * @param entity An entity identifier, either valid or not. - * @return The entity identifier without the version. - */ - [[nodiscard]] static entity_type entity(const entity_type entity) ENTT_NOEXCEPT { - return entity_type{to_integral(entity) & traits_type::entity_mask}; - } - - /** - * @brief Returns the version stored along with an entity identifier. - * @param entity An entity identifier, either valid or not. - * @return The version stored along with the given entity identifier. - */ - [[nodiscard]] static version_type version(const entity_type entity) ENTT_NOEXCEPT { - return version_type(to_integral(entity) >> traits_type::entity_shift); - } - /** * @brief Returns the actual version for an entity identifier. * * @warning * Attempting to use an entity that doesn't belong to the registry results * in undefined behavior. An entity belongs to the registry even if it has - * been previously destroyed and/or recycled.
- * An assertion will abort the execution at runtime in debug mode if the - * registry doesn't own the given entity. + * been previously destroyed and/or recycled. * * @param entity A valid entity identifier. * @return Actual version for the given entity identifier. @@ -8011,7 +17116,7 @@ public: [[nodiscard]] version_type current(const entity_type entity) const { const auto pos = size_type(to_integral(entity) & traits_type::entity_mask); ENTT_ASSERT(pos < entities.size()); - return version_type(to_integral(entities[pos]) >> traits_type::entity_shift); + return version(entities[pos]); } /** @@ -8025,20 +17130,7 @@ public: * @return A valid entity identifier. */ entity_type create() { - entity_type entt; - - if(destroyed == null) { - entt = entities.emplace_back(entity_type{static_cast(entities.size())}); - // traits_type::entity_mask is reserved to allow for null identifiers - ENTT_ASSERT(to_integral(entt) < traits_type::entity_mask); - } else { - const auto curr = to_integral(destroyed); - const auto version = to_integral(entities[curr]) & (traits_type::version_mask << traits_type::entity_shift); - destroyed = entity_type{to_integral(entities[curr]) & traits_type::entity_mask}; - entt = entities[curr] = entity_type{curr | version}; - } - - return entt; + return available == null ? generate_identifier() : recycle_identifier(); } /** @@ -8057,18 +17149,17 @@ public: entity_type entt; if(const auto req = (to_integral(hint) & traits_type::entity_mask); !(req < entities.size())) { - entities.reserve(req + 1); + entities.reserve(size_type(req) + 1u); for(auto pos = entities.size(); pos < req; ++pos) { - entities.emplace_back(destroyed); - destroyed = entity_type{static_cast(pos)}; + release_entity(generate_identifier(), {}); } entt = entities.emplace_back(hint); } else if(const auto curr = (to_integral(entities[req]) & traits_type::entity_mask); req == curr) { entt = create(); } else { - auto *it = &destroyed; + auto *it = &available; for(; (to_integral(*it) & traits_type::entity_mask) != req; it = &entities[to_integral(*it) & traits_type::entity_mask]); *it = entity_type{curr | (to_integral(*it) & (traits_type::version_mask << traits_type::entity_shift))}; entt = entities[req] = hint; @@ -8088,37 +17179,37 @@ public: */ template void create(It first, It last) { - std::generate(first, last, [this]() { return create(); }); + for(; available != null && first != last; ++first) { + *first = recycle_identifier(); + } + + for(; first != last; ++first) { + *first = generate_identifier(); + } } /** * @brief Assigns entities to an empty registry. * - * This function is intended for use in conjunction with `raw`.
- * Don't try to inject ranges of randomly generated entities. There is no - * guarantee that a registry will continue to work properly in this case. + * This function is intended for use in conjunction with `data`, `size` and + * `destroyed`.
+ * 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 - * An assertion will abort the execution at runtime in debug mode if all - * pools aren't empty. + * 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 - void assign(It first, It last) { - ENTT_ASSERT(std::all_of(pools.cbegin(), pools.cend(), [](auto &&pdata) { return !pdata.pool || pdata.pool->empty(); })); + void assign(It first, It last, const entity_type destroyed) { + ENTT_ASSERT(!alive()); entities.assign(first, last); - destroyed = null; - - for(std::size_t pos{}, end = entities.size(); pos < end; ++pos) { - if((to_integral(entities[pos]) & traits_type::entity_mask) != pos) { - const auto version = to_integral(entities[pos]) & (traits_type::version_mask << traits_type::entity_shift); - entities[pos] = entity_type{to_integral(destroyed) | version}; - destroyed = entity_type{static_cast(pos)}; - } - } + available = destroyed; } /** @@ -8132,7 +17223,7 @@ public: * @param entity A valid entity identifier. */ void destroy(const entity_type entity) { - destroy(entity, version_type((to_integral(entity) >> traits_type::entity_shift) + 1)); + destroy(entity, static_cast(version(entity) + 1u)); } /** @@ -8148,10 +17239,7 @@ public: */ void destroy(const entity_type entity, const version_type version) { remove_all(entity); - // lengthens the implicit list of destroyed entities - const auto entt = to_integral(entity) & traits_type::entity_mask; - entities[entt] = entity_type{to_integral(destroyed) | (typename traits_type::entity_type{version} << traits_type::entity_shift)}; - destroyed = entity_type{entt}; + release_entity(entity, version); } /** @@ -8165,7 +17253,9 @@ public: */ template void destroy(It first, It last) { - while(first != last) { destroy(*(first++)); } + for(; first != last; ++first) { + destroy(*first); + } } /** @@ -8177,10 +17267,7 @@ public: * * @warning * Attempting to use an invalid entity or to assign a component to an entity - * that already owns it results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity or if the entity already owns an instance of the given - * component. + * 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. @@ -8191,7 +17278,7 @@ public: template decltype(auto) emplace(const entity_type entity, Args &&... args) { ENTT_ASSERT(valid(entity)); - return assure().emplace(*this, entity, std::forward(args)...); + return assure()->emplace(*this, entity, std::forward(args)...); } /** @@ -8208,7 +17295,7 @@ public: template void insert(It first, It last, const Component &value = {}) { ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); })); - assure().insert(*this, first, last, value); + assure()->insert(*this, first, last, value); } /** @@ -8228,7 +17315,7 @@ public: void insert(EIt first, EIt last, CIt from, CIt to) { static_assert(std::is_constructible_v::value_type>, "Invalid value type"); ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); })); - assure().insert(*this, first, last, from, to); + assure()->insert(*this, first, last, from, to); } /** @@ -8237,15 +17324,13 @@ public: * Equivalent to the following snippet (pseudocode): * * @code{.cpp} - * auto &component = registry.has(entity) ? registry.replace(entity, args...) : registry.emplace(entity, args...); + * auto &component = registry.all_of(entity) ? registry.replace(entity, args...) : registry.emplace(entity, args...); * @endcode * * Prefer this function anyway because it has slightly better performance. * * @warning - * Attempting to use an invalid entity results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity. + * 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. @@ -8256,11 +17341,11 @@ public: template decltype(auto) emplace_or_replace(const entity_type entity, Args &&... args) { ENTT_ASSERT(valid(entity)); - auto &cpool = assure(); + auto *cpool = assure(); - return cpool.contains(entity) - ? cpool.replace(*this, entity, Component{std::forward(args)...}) - : cpool.emplace(*this, entity, std::forward(args)...); + return cpool->contains(entity) + ? cpool->patch(*this, entity, [&args...](auto &... curr) { ((curr = Component{std::forward(args)...}), ...); }) + : cpool->emplace(*this, entity, std::forward(args)...); } /** @@ -8279,10 +17364,7 @@ public: * * @warning * Attempting to use an invalid entity or to patch a component of an entity - * that doesn't own it results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity or if the entity doesn't own an instance of the given - * component. + * 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. @@ -8293,7 +17375,7 @@ public: template decltype(auto) patch(const entity_type entity, Func &&... func) { ENTT_ASSERT(valid(entity)); - return assure().patch(*this, entity, std::forward(func)...); + return assure()->patch(*this, entity, std::forward(func)...); } /** @@ -8305,10 +17387,7 @@ public: * * @warning * Attempting to use an invalid entity or to replace a component of an - * entity that doesn't own it results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity or if the entity doesn't own an instance of the given - * component. + * 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. @@ -8318,7 +17397,7 @@ public: */ template decltype(auto) replace(const entity_type entity, Args &&... args) { - return assure().replace(*this, entity, Component{std::forward(args)...}); + return assure()->patch(*this, entity, [&args...](auto &... curr) { ((curr = Component{std::forward(args)...}), ...); }); } /** @@ -8326,10 +17405,7 @@ public: * * @warning * Attempting to use an invalid entity or to remove a component from an - * entity that doesn't own it results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity or if the entity doesn't own an instance of the given - * component. + * entity that doesn't own it results in undefined behavior. * * @tparam Component Types of components to remove. * @param entity A valid entity identifier. @@ -8337,13 +17413,14 @@ public: template void remove(const entity_type entity) { ENTT_ASSERT(valid(entity)); - (assure().remove(*this, entity), ...); + static_assert(sizeof...(Component) > 0, "Provide one or more component types"); + (assure()->remove(entity, this), ...); } /** * @brief Removes the given components from all the entities in a range. * - * @see remove + * @sa remove * * @tparam Component Types of components to remove. * @tparam It Type of input iterator. @@ -8353,7 +17430,8 @@ public: template void remove(It first, It last) { ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); })); - (assure().remove(*this, first, last), ...); + static_assert(sizeof...(Component) > 0, "Provide one or more component types"); + (assure()->remove(first, last, this), ...); } /** @@ -8362,15 +17440,13 @@ public: * Equivalent to the following snippet (pseudocode): * * @code{.cpp} - * if(registry.has(entity)) { registry.remove(entity) } + * if(registry.all_of(entity)) { registry.remove(entity) } * @endcode * * Prefer this function anyway because it has slightly better performance. * * @warning - * Attempting to use an invalid entity results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity. + * Attempting to use an invalid entity results in undefined behavior. * * @tparam Component Types of components to remove. * @param entity A valid entity identifier. @@ -8380,8 +17456,8 @@ public: size_type remove_if_exists(const entity_type entity) { ENTT_ASSERT(valid(entity)); - return ([this, entity](auto &&cpool) { - return cpool.contains(entity) ? (cpool.remove(*this, entity), true) : false; + return ([this, entity](auto *cpool) { + return cpool->contains(entity) ? (cpool->remove(entity, this), true) : false; }(assure()) + ... + size_type{}); } @@ -8395,18 +17471,17 @@ public: * could lead to undefined behavior. * * @warning - * Attempting to use an invalid entity results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity. + * Attempting to use an invalid entity results in undefined behavior. * * @param entity A valid entity identifier. */ void remove_all(const entity_type entity) { ENTT_ASSERT(valid(entity)); + entity_type wrap[1u]{entity}; for(auto pos = pools.size(); pos; --pos) { if(auto &pdata = pools[pos-1]; pdata.pool && pdata.pool->contains(entity)) { - pdata.remove(*pdata.pool, *this, entity); + pdata.pool->remove(std::begin(wrap), std::end(wrap), this); } } } @@ -8415,27 +17490,23 @@ public: * @brief Checks if an entity has all the given components. * * @warning - * Attempting to use an invalid entity results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity. + * Attempting to use an invalid entity results in undefined behavior. * * @tparam Component Components for which to perform the check. * @param entity A valid entity identifier. * @return True if the entity has all the components, false otherwise. */ template - [[nodiscard]] bool has(const entity_type entity) const { + [[nodiscard]] bool all_of(const entity_type entity) const { ENTT_ASSERT(valid(entity)); - return (assure().contains(entity) && ...); + return [entity](const auto *... cpool) { return ((cpool && cpool->contains(entity)) && ...); }(pool_if_exists()...); } /** * @brief Checks if an entity has at least one of the given components. * * @warning - * Attempting to use an invalid entity results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity. + * Attempting to use an invalid entity results in undefined behavior. * * @tparam Component Components for which to perform the check. * @param entity A valid entity identifier. @@ -8443,9 +17514,9 @@ public: * false otherwise. */ template - [[nodiscard]] bool any(const entity_type entity) const { + [[nodiscard]] bool any_of(const entity_type entity) const { ENTT_ASSERT(valid(entity)); - return (assure().contains(entity) || ...); + return [entity](const auto *... cpool) { return !((!cpool || !cpool->contains(entity)) && ...); }(pool_if_exists()...); } /** @@ -8453,10 +17524,7 @@ public: * * @warning * Attempting to use an invalid entity or to get a component from an entity - * that doesn't own it results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity or if the entity doesn't own an instance of the given - * component. + * that doesn't own it results in undefined behavior. * * @tparam Component Types of components to get. * @param entity A valid entity identifier. @@ -8467,7 +17535,9 @@ public: ENTT_ASSERT(valid(entity)); if constexpr(sizeof...(Component) == 1) { - return (assure().get(entity), ...); + const auto *cpool = pool_if_exists...>(); + ENTT_ASSERT(cpool); + return cpool->get(entity); } else { return std::forward_as_tuple(get(entity)...); } @@ -8479,7 +17549,7 @@ public: ENTT_ASSERT(valid(entity)); if constexpr(sizeof...(Component) == 1) { - return (assure().get(entity), ...); + return (const_cast(assure>()->get(entity)), ...); } else { return std::forward_as_tuple(get(entity)...); } @@ -8493,15 +17563,13 @@ public: * Equivalent to the following snippet (pseudocode): * * @code{.cpp} - * auto &component = registry.has(entity) ? registry.get(entity) : registry.emplace(entity, args...); + * auto &component = registry.all_of(entity) ? registry.get(entity) : registry.emplace(entity, args...); * @endcode * * Prefer this function anyway because it has slightly better performance. * * @warning - * Attempting to use an invalid entity results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity. + * 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. @@ -8512,17 +17580,18 @@ public: template [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entity, Args &&... args) { ENTT_ASSERT(valid(entity)); - auto &cpool = assure(); - return cpool.contains(entity) ? cpool.get(entity) : cpool.emplace(*this, entity, std::forward(args)...); + auto *cpool = assure(); + return cpool->contains(entity) ? cpool->get(entity) : cpool->emplace(*this, entity, std::forward(args)...); } /** * @brief Returns pointers to the given components for an entity. * * @warning - * Attempting to use an invalid entity results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity. + * 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 entity identifier. @@ -8533,7 +17602,8 @@ public: ENTT_ASSERT(valid(entity)); if constexpr(sizeof...(Component) == 1) { - return (assure().try_get(entity), ...); + const auto *cpool = pool_if_exists...>(); + return (cpool && cpool->contains(entity)) ? &cpool->get(entity) : nullptr; } else { return std::make_tuple(try_get(entity)...); } @@ -8545,7 +17615,7 @@ public: ENTT_ASSERT(valid(entity)); if constexpr(sizeof...(Component) == 1) { - return (assure().try_get(entity), ...); + return (const_cast(std::as_const(*this).template try_get(entity)), ...); } else { return std::make_tuple(try_get(entity)...); } @@ -8558,11 +17628,16 @@ public: template void clear() { if constexpr(sizeof...(Component) == 0) { - // useless this-> used to suppress a warning with clang - each([this](const auto entity) { this->destroy(entity); }); + for(auto pos = pools.size(); pos; --pos) { + if(auto &pdata = pools[pos-1]; pdata.pool) { + pdata.pool->clear(this); + } + } + + each([this](const auto entity) { release_entity(entity, version(entity) + 1u); }); } else { - ([this](auto &&cpool) { - cpool.remove(*this, cpool.sparse_set::begin(), cpool.sparse_set::end()); + ([this](auto *cpool) { + cpool->remove(cpool->basic_sparse_set::begin(), cpool->basic_sparse_set::end(), this); }(assure()), ...); } } @@ -8586,14 +17661,14 @@ public: */ template void each(Func func) const { - if(destroyed == null) { + if(available == null) { for(auto pos = entities.size(); pos; --pos) { func(entities[pos-1]); } } else { for(auto pos = entities.size(); pos; --pos) { - if(const auto entt = entities[pos - 1]; (to_integral(entt) & traits_type::entity_mask) == (pos - 1)) { - func(entt); + if(const auto entity = entities[pos - 1]; (to_integral(entity) & traits_type::entity_mask) == (pos - 1)) { + func(entity); } } } @@ -8637,15 +17712,13 @@ public: /** * @brief Returns a sink object for the given component. * - * A sink is an opaque object used to connect listeners to components.
* The sink returned by this function can be used to receive notifications * whenever a new instance of the given component is created and assigned to - * an entity. - * + * an entity.
* The function type for a listener is equivalent to: * * @code{.cpp} - * void(registry &, Entity); + * void(basic_registry &, Entity); * @endcode * * Listeners are invoked **after** the component has been assigned to the @@ -8658,20 +17731,18 @@ public: */ template [[nodiscard]] auto on_construct() { - return assure().on_construct(); + return assure()->on_construct(); } /** * @brief Returns a sink object for the given component. * - * A sink is an opaque object used to connect listeners to components.
* The sink returned by this function can be used to receive notifications - * whenever an instance of the given component is explicitly updated. - * + * whenever an instance of the given component is explicitly updated.
* The function type for a listener is equivalent to: * * @code{.cpp} - * void(registry &, Entity); + * void(basic_registry &, Entity); * @endcode * * Listeners are invoked **after** the component has been updated. @@ -8683,21 +17754,19 @@ public: */ template [[nodiscard]] auto on_update() { - return assure().on_update(); + return assure()->on_update(); } /** * @brief Returns a sink object for the given component. * - * A sink is an opaque object used to connect listeners to components.
* The sink returned by this function can be used to receive notifications * whenever an instance of the given component is removed from an entity and - * thus destroyed. - * + * thus destroyed.
* The function type for a listener is equivalent to: * * @code{.cpp} - * void(registry &, Entity); + * void(basic_registry &, Entity); * @endcode * * Listeners are invoked **before** the component has been removed from the @@ -8710,7 +17779,7 @@ public: */ template [[nodiscard]] auto on_destroy() { - return assure().on_destroy(); + return assure()->on_destroy(); } /** @@ -8748,14 +17817,15 @@ public: template [[nodiscard]] basic_view, Component...> view(exclude_t = {}) const { static_assert(sizeof...(Component) > 0, "Exclusion-only views are not supported"); - return { assure>()..., assure()... }; + static_assert((std::is_const_v && ...), "Invalid non-const type"); + return { *assure>()..., *assure()... }; } /*! @copydoc view */ template [[nodiscard]] basic_view, Component...> view(exclude_t = {}) { static_assert(sizeof...(Component) > 0, "Exclusion-only views are not supported"); - return { assure>()..., assure()... }; + return { *assure>()..., *assure()... }; } /** @@ -8788,16 +17858,16 @@ public: */ template [[nodiscard]] basic_runtime_view runtime_view(ItComp first, ItComp last, ItExcl from = {}, ItExcl to = {}) const { - std::vector *> component(std::distance(first, last)); - std::vector *> filter(std::distance(from, to)); + std::vector *> component(std::distance(first, last)); + std::vector *> filter(std::distance(from, to)); std::transform(first, last, component.begin(), [this](const auto ctype) { - const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.pool && pdata.type_id == ctype; }); + const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.poly && pdata.poly->value_type().hash() == ctype; }); return it == pools.cend() ? nullptr : it->pool.get(); }); std::transform(from, to, filter.begin(), [this](const auto ctype) { - const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.pool && pdata.type_id == ctype; }); + const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.poly && pdata.poly->value_type().hash() == ctype; }); return it == pools.cend() ? nullptr : it->pool.get(); }); @@ -8833,20 +17903,20 @@ public: */ template [[nodiscard]] basic_group, get_t, Owned...> group(get_t, exclude_t = {}) { - static_assert(sizeof...(Owned) + sizeof...(Get) > 0, "Exclusion-only views are not supported"); + 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, get_t...>, std::decay_t...>; + using handler_type = group_handler, get_t...>, std::remove_const_t...>; - const auto cpools = std::forward_as_tuple(assure>()..., assure>()...); + const auto cpools = std::make_tuple(assure>()..., assure>()...); constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); handler_type *handler = nullptr; if(auto it = std::find_if(groups.cbegin(), groups.cend(), [size](const auto &gdata) { return gdata.size == size - && (gdata.owned(type_info>::id()) && ...) - && (gdata.get(type_info>::id()) && ...) - && (gdata.exclude(type_info::id()) && ...); + && (gdata.owned(type_hash>::value()) && ...) + && (gdata.get(type_hash>::value()) && ...) + && (gdata.exclude(type_hash::value()) && ...); }); it != groups.cend()) { handler = static_cast(it->group.get()); @@ -8856,9 +17926,9 @@ public: group_data candidate = { size, { new handler_type{}, [](void *instance) { delete static_cast(instance); } }, - []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_info>::id()) || ...); }, - []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_info>::id()) || ...); }, - []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_info::id()) || ...); }, + []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash>::value()) || ...); }, + []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash>::value()) || ...); }, + []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash::value()) || ...); }, }; handler = static_cast(candidate.group.get()); @@ -8870,17 +17940,17 @@ public: groups.push_back(std::move(candidate)); } else { ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [size](const auto &gdata) { - const auto overlapping = (0u + ... + gdata.owned(type_info>::id())); - const auto sz = overlapping + (0u + ... + gdata.get(type_info>::id())) + (0u + ... + gdata.exclude(type_info::id())); + const auto overlapping = (0u + ... + gdata.owned(type_hash>::value())); + const auto sz = overlapping + (0u + ... + gdata.get(type_hash>::value())) + (0u + ... + gdata.exclude(type_hash::value())); return !overlapping || ((sz == size) || (sz == gdata.size)); })); const auto next = std::find_if_not(groups.cbegin(), groups.cend(), [size](const auto &gdata) { - return !(0u + ... + gdata.owned(type_info>::id())) || (size > gdata.size); + return !(0u + ... + gdata.owned(type_hash>::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_info>::id())); + return (0u + ... + gdata.owned(type_hash>::value())); }); maybe_valid_if = (next == groups.cend() ? maybe_valid_if : next->group.get()); @@ -8888,12 +17958,12 @@ public: groups.insert(next, std::move(candidate)); } - (on_construct>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); - (on_construct>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); + (on_construct>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); + (on_construct>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); (on_destroy().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>(*handler), ...); - (on_destroy>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); - (on_destroy>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); + (on_destroy>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); + (on_destroy>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); (on_construct().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); if constexpr(sizeof...(Owned) == 0) { @@ -8902,17 +17972,13 @@ public: } } 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...>>>(*this, *first); + for(auto *first = std::get<0>(cpools)->data(), *last = first + std::get<0>(cpools)->size(); first != last; ++first) { + handler->template maybe_valid_if...>>>(*this, *first); } } } - if constexpr(sizeof...(Owned) == 0) { - return { handler->current, std::get> &>(cpools)... }; - } else { - return { handler->current, std::get> &>(cpools)... , std::get> &>(cpools)... }; - } + return { handler->current, *std::get> *>(cpools)..., *std::get> *>(cpools)... }; } /** @@ -8926,9 +17992,21 @@ public: * @return A newly created group. */ template - [[nodiscard]] basic_group, get_t, Owned...> group(get_t, exclude_t = {}) const { + [[nodiscard]] basic_group, get_t, Owned...> group_if_exists(get_t, exclude_t = {}) const { static_assert(std::conjunction_v..., std::is_const...>, "Invalid non-const type"); - return const_cast(this)->group(get_t{}, exclude); + + if(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>::value()) && ...) + && (gdata.get(type_hash>::value()) && ...) + && (gdata.exclude(type_hash::value()) && ...); + }); it == groups.cend()) + { + return {}; + } else { + using handler_type = group_handler, get_t...>, std::remove_const_t...>; + return { static_cast(it->group.get())->current, *pool_if_exists>()... , *pool_if_exists>()... }; + } } /** @@ -8948,16 +18026,15 @@ public: /** * @brief Returns a group for the given components. * - * @sa group + * @sa group_if_exists * * @tparam Owned Types of components owned by the group. * @tparam Exclude Types of components used to filter the group. * @return A newly created group. */ template - [[nodiscard]] basic_group, get_t<>, Owned...> group(exclude_t = {}) const { - static_assert(std::conjunction_v...>, "Invalid non-const type"); - return const_cast(this)->group(exclude); + [[nodiscard]] basic_group, get_t<>, Owned...> group_if_exists(exclude_t = {}) const { + return group_if_exists(get_t<>{}, exclude); } /** @@ -8968,7 +18045,7 @@ public: */ template [[nodiscard]] bool sortable() const { - return std::none_of(groups.cbegin(), groups.cend(), [](auto &&gdata) { return (gdata.owned(type_info>::id()) || ...); }); + return std::none_of(groups.cbegin(), groups.cend(), [](auto &&gdata) { return (gdata.owned(type_hash>::value()) || ...); }); } /** @@ -8982,7 +18059,7 @@ public: [[nodiscard]] bool sortable(const basic_group, get_t, Owned...> &) ENTT_NOEXCEPT { constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); return std::find_if(groups.cbegin(), groups.cend(), [size](const auto &gdata) { - return (0u + ... + gdata.owned(type_info>::id())) && (size < gdata.size); + return (0u + ... + gdata.owned(type_hash>::value())) && (size < gdata.size); }) == groups.cend(); } @@ -9021,9 +18098,7 @@ public: * this member function. * * @warning - * Pools of components owned by a group cannot be sorted.
- * An assertion will abort the execution at runtime in debug mode in case - * the pool is owned by a group. + * Pools of components owned by a group cannot be sorted. * * @tparam Component Type of components to sort. * @tparam Compare Type of comparison function object. @@ -9036,8 +18111,7 @@ public: template void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { ENTT_ASSERT(sortable()); - auto &cpool = assure(); - cpool.sort(cpool.begin(), cpool.end(), std::move(compare), std::move(algo), std::forward(args)...); + assure()->sort(std::move(compare), std::move(algo), std::forward(args)...); } /** @@ -9068,9 +18142,7 @@ public: * Any subsequent change to `B` won't affect the order in `A`. * * @warning - * Pools of components owned by a group cannot be sorted.
- * An assertion will abort the execution at runtime in debug mode in case - * the pool is owned by a group. + * 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. @@ -9078,16 +18150,16 @@ public: template void sort() { ENTT_ASSERT(sortable()); - assure().respect(assure()); + assure()->respect(*assure()); } /** - * @brief Visits an entity and returns the types for its components. + * @brief Visits an entity and returns the type info for its components. * * The signature of the function should be equivalent to the following: * * @code{.cpp} - * void(const id_type); + * void(const type_info); * @endcode * * Returned identifiers are those of the components owned by the entity. @@ -9106,18 +18178,18 @@ public: void visit(entity_type entity, Func func) const { for(auto pos = pools.size(); pos; --pos) { if(const auto &pdata = pools[pos-1]; pdata.pool && pdata.pool->contains(entity)) { - func(pdata.type_id); + func(pdata.poly->value_type()); } } } /** - * @brief Visits a registry and returns the types for its components. + * @brief Visits a registry and returns the type info for its components. * * The signature of the function should be equivalent to the following: * * @code{.cpp} - * void(const id_type); + * void(const type_info); * @endcode * * Returned identifiers are those of the components managed by the registry. @@ -9135,7 +18207,7 @@ public: void visit(Func func) const { for(auto pos = pools.size(); pos; --pos) { if(const auto &pdata = pools[pos-1]; pdata.pool) { - func(pdata.type_id); + func(pdata.poly->value_type()); } } } @@ -9154,8 +18226,8 @@ public: template Type & set(Args &&... args) { unset(); - vars.push_back(variable_data{type_info::id(), { new Type{std::forward(args)...}, [](void *instance) { delete static_cast(instance); } }}); - return *static_cast(vars.back().value.get()); + vars.emplace_back(std::in_place_type, std::forward(args)...); + return any_cast(vars.back()); } /** @@ -9164,9 +18236,7 @@ public: */ template void unset() { - vars.erase(std::remove_if(vars.begin(), vars.end(), [](auto &&var) { - return var.type_id == type_info::id(); - }), vars.end()); + vars.erase(std::remove_if(vars.begin(), vars.end(), [type = type_id()](auto &&var) { return var.type() == type; }), vars.end()); } /** @@ -9193,15 +18263,16 @@ public: * registry, a null pointer otherwise. */ template - [[nodiscard]] const Type * try_ctx() const { - auto it = std::find_if(vars.cbegin(), vars.cend(), [](auto &&var) { return var.type_id == type_info::id(); }); - return it == vars.cend() ? nullptr : static_cast(it->value.get()); + [[nodiscard]] Type * try_ctx() const { + auto it = std::find_if(vars.cbegin(), vars.cend(), [type = type_id()](auto &&var) { return var.type() == type; }); + return it == vars.cend() ? nullptr : any_cast(&*it); } /*! @copydoc try_ctx */ template [[nodiscard]] Type * try_ctx() { - return const_cast(std::as_const(*this).template try_ctx()); + auto it = std::find_if(vars.begin(), vars.end(), [type = type_id()](auto &&var) { return var.type() == type; }); + return it == vars.end() ? nullptr : any_cast(&*it); } /** @@ -9209,33 +18280,34 @@ public: * * @warning * Attempting to get a context variable that doesn't exist results in - * undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid requests. + * undefined behavior. * * @tparam Type Type of object to get. * @return A valid reference to the object in the context of the registry. */ template - [[nodiscard]] const Type & ctx() const { - const auto *instance = try_ctx(); - ENTT_ASSERT(instance); - return *instance; + [[nodiscard]] Type & ctx() const { + auto it = std::find_if(vars.cbegin(), vars.cend(), [type = type_id()](auto &&var) { return var.type() == type; }); + ENTT_ASSERT(it != vars.cend()); + return any_cast(*it); } /*! @copydoc ctx */ template [[nodiscard]] Type & ctx() { - return const_cast(std::as_const(*this).template ctx()); + auto it = std::find_if(vars.begin(), vars.end(), [type = type_id()](auto &&var) { return var.type() == type; }); + ENTT_ASSERT(it != vars.end()); + return any_cast(*it); } /** - * @brief Visits a registry and returns the types for its context variables. + * @brief Visits a registry and returns the type info for its context + * variables. * * The signature of the function should be equivalent to the following: * * @code{.cpp} - * void(const id_type); + * void(const type_info); * @endcode * * Returned identifiers are those of the context variables currently set. @@ -9252,16 +18324,16 @@ public: template void ctx(Func func) const { for(auto pos = vars.size(); pos; --pos) { - func(vars[pos-1].type_id); + func(vars[pos-1].type()); } } private: - std::vector groups{}; + std::vector> vars{}; mutable std::vector pools{}; + std::vector groups{}; std::vector entities{}; - std::vector vars{}; - entity_type destroyed{null}; + entity_type available{null}; }; @@ -9270,218 +18342,6 @@ private: #endif -// #include "entity.hpp" - -// #include "fwd.hpp" - - - -namespace entt { - - -/** - * @brief Dedicated to those who aren't confident with the - * entity-component-system architecture. - * - * Tiny wrapper around a registry, for all those users that aren't confident - * with entity-component-system architecture and prefer to iterate objects - * directly. - * - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -struct [[deprecated("Consider using the handle class instead")]] basic_actor { - /*! @brief Type of registry used internally. */ - using registry_type = basic_registry; - /*! @brief Underlying entity identifier. */ - using entity_type = Entity; - - basic_actor() ENTT_NOEXCEPT - : entt{null}, reg{nullptr} - {} - - /** - * @brief Move constructor. - * - * After actor move construction, instances that have been moved from are - * placed in a valid but unspecified state. It's highly discouraged to - * continue using them. - * - * @param other The instance to move from. - */ - basic_actor(basic_actor &&other) ENTT_NOEXCEPT - : entt{other.entt}, reg{other.reg} - { - other.entt = null; - } - - /** - * @brief Constructs an actor from a given registry. - * @param ref An instance of the registry class. - */ - explicit basic_actor(registry_type &ref) - : entt{ref.create()}, reg{&ref} - {} - - /** - * @brief Constructs an actor from a given entity. - * @param entity A valid entity identifier. - * @param ref An instance of the registry class. - */ - explicit basic_actor(entity_type entity, registry_type &ref) ENTT_NOEXCEPT - : entt{entity}, reg{&ref} - { - ENTT_ASSERT(ref.valid(entity)); - } - - /*! @brief Default destructor. */ - virtual ~basic_actor() { - if(*this) { - reg->destroy(entt); - } - } - - /** - * @brief Move assignment operator. - * - * After actor move assignment, instances that have been moved from are - * placed in a valid but unspecified state. It's highly discouraged to - * continue using them. - * - * @param other The instance to move from. - * @return This actor. - */ - basic_actor & operator=(basic_actor &&other) ENTT_NOEXCEPT { - if(this != &other) { - auto tmp{std::move(other)}; - std::swap(reg, tmp.reg); - std::swap(entt, tmp.entt); - } - - return *this; - } - - /** - * @brief Assigns the given component to an actor. - * - * A new instance of the given component is created and initialized with the - * arguments provided (the component must have a proper constructor or be of - * aggregate type). Then the component is assigned to the actor.
- * In case the actor already has a component of the given type, it's - * replaced with the new one. - * - * @tparam Component Type of the component to create. - * @tparam Args Types of arguments to use to construct the component. - * @param args Parameters to use to initialize the component. - * @return A reference to the newly created component. - */ - template - decltype(auto) assign(Args &&... args) { - return reg->template emplace_or_replace(entt, std::forward(args)...); - } - - /** - * @brief Removes the given component from an actor. - * @tparam Component Type of the component to remove. - */ - template - void remove() { - reg->template remove(entt); - } - - /** - * @brief Checks if an actor has the given components. - * @tparam Component Components for which to perform the check. - * @return True if the actor has all the components, false otherwise. - */ - template - [[nodiscard]] bool has() const { - return reg->template has(entt); - } - - /** - * @brief Returns references to the given components for an actor. - * @tparam Component Types of components to get. - * @return References to the components owned by the actor. - */ - template - [[nodiscard]] decltype(auto) get() const { - return std::as_const(*reg).template get(entt); - } - - /*! @copydoc get */ - template - [[nodiscard]] decltype(auto) get() { - return reg->template get(entt); - } - - /** - * @brief Returns pointers to the given components for an actor. - * @tparam Component Types of components to get. - * @return Pointers to the components owned by the actor. - */ - template - [[nodiscard]] auto try_get() const { - return std::as_const(*reg).template try_get(entt); - } - - /*! @copydoc try_get */ - template - [[nodiscard]] auto try_get() { - return reg->template try_get(entt); - } - - /** - * @brief Returns a reference to the underlying registry. - * @return A reference to the underlying registry. - */ - [[nodiscard]] const registry_type & backend() const ENTT_NOEXCEPT { - return *reg; - } - - /*! @copydoc backend */ - [[nodiscard]] registry_type & backend() ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).backend()); - } - - /** - * @brief Returns the entity associated with an actor. - * @return The entity associated with the actor. - */ - [[nodiscard]] entity_type entity() const ENTT_NOEXCEPT { - return entt; - } - - /** - * @brief Checks if an actor refers to a valid entity or not. - * @return True if the actor refers to a valid entity, false otherwise. - */ - [[nodiscard]] explicit operator bool() const { - return reg && reg->valid(entt); - } - -private: - entity_type entt; - registry_type *reg; -}; - - -} - - -#endif - -// #include "entity/entity.hpp" - -// #include "entity/group.hpp" - -// #include "entity/handle.hpp" -#ifndef ENTT_ENTITY_HANDLE_HPP -#define ENTT_ENTITY_HANDLE_HPP - - -// #include "registry.hpp" - namespace entt { @@ -9493,52 +18353,57 @@ namespace entt { * 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 +template struct basic_handle { /*! @brief Underlying entity identifier. */ using entity_type = std::remove_const_t; - /*! @brief Type of registry accepted by the handle. */ - using registry_type = std::conditional_t< - std::is_const_v, - const basic_registry, - basic_registry - >; + using registry_type = constness_as_t, Entity>; + + /*! @brief Constructs an invalid handle. */ + basic_handle() ENTT_NOEXCEPT + : reg{}, entt{null} + {} /** * @brief Constructs a handle from a given registry and entity. * @param ref An instance of the registry class. * @param value An entity identifier. */ - basic_handle(registry_type &ref, entity_type value = null) ENTT_NOEXCEPT + basic_handle(registry_type &ref, entity_type value) ENTT_NOEXCEPT : reg{&ref}, entt{value} {} /** - * @brief Assigns an entity to a handle. - * @param value An entity identifier. - * @return This handle. + * @brief Compares two handles. + * @tparam Args Template parameters of the handle with which to compare. + * @param other Handle with which to compare. + * @return True if both handles refer to the same registry and the same + * entity, false otherwise. */ - basic_handle & operator=(const entity_type value) ENTT_NOEXCEPT { - entt = value; - return *this; - } - - /** - * @brief Assigns the null object to a handle. - * @return This handle. - */ - basic_handle & operator=(null_t) ENTT_NOEXCEPT { - return (*this = static_cast(null)); + template + [[nodiscard]] bool operator==(const basic_handle &other) const ENTT_NOEXCEPT { + return reg == other.registry() && entt == other.entity(); } /** * @brief Constructs a const handle from a non-const one. - * @return A const handle referring to the same entity. + * @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. */ - [[nodiscard]] operator basic_handle() const ENTT_NOEXCEPT { - return {*reg, entt}; + template + operator basic_handle() const ENTT_NOEXCEPT { + static_assert( + (std::is_same_v || std::is_same_v, Entity>) + && (sizeof...(Type) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Type)) && ... && (type_list_contains_v, Args>))), + "Invalid conversion between different handles" + ); + + return reg ? basic_handle{*reg, entt} : basic_handle{}; } /** @@ -9549,20 +18414,28 @@ struct basic_handle { 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]] explicit operator bool() const { + [[nodiscard]] bool valid() const { return reg->valid(entt); } /** - * @brief Returns a reference to the underlying registry. - * @return A reference to the underlying registry. + * @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; + [[nodiscard]] registry_type * registry() const ENTT_NOEXCEPT { + return reg; } /** @@ -9573,6 +18446,23 @@ struct basic_handle { return entt; } + /** + * @brief Destroys the entity associated with a handle. + * @sa basic_registry::destroy + */ + void destroy() { + reg->destroy(entt); + } + + /** + * @brief Destroys the entity associated with a handle. + * @sa basic_registry::destroy + * @param version A desired version upon destruction. + */ + void destroy(const typename registry_type::version_type version) { + reg->destroy(entt, version); + } + /** * @brief Assigns the given component to a handle. * @sa basic_registry::emplace @@ -9583,6 +18473,7 @@ struct basic_handle { */ template decltype(auto) emplace(Args &&... args) const { + static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v), "Invalid type"); return reg->template emplace(entt, std::forward(args)...); } @@ -9596,6 +18487,7 @@ struct basic_handle { */ template decltype(auto) emplace_or_replace(Args &&... args) const { + static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v), "Invalid type"); return reg->template emplace_or_replace(entt, std::forward(args)...); } @@ -9609,6 +18501,7 @@ struct basic_handle { */ template decltype(auto) patch(Func &&... func) const { + static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v), "Invalid type"); return reg->template patch(entt, std::forward(func)...); } @@ -9622,6 +18515,7 @@ struct basic_handle { */ template decltype(auto) replace(Args &&... args) const { + static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v), "Invalid type"); return reg->template replace(entt, std::forward(args)...); } @@ -9630,9 +18524,10 @@ struct basic_handle { * @sa basic_registry::remove * @tparam Component Types of components to remove. */ - template + template void remove() const { - reg->template remove(entt); + static_assert(sizeof...(Type) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + reg->template remove(entt); } /** @@ -9641,9 +18536,10 @@ struct basic_handle { * @tparam Component Types of components to remove. * @return The number of components actually removed. */ - template + template decltype(auto) remove_if_exists() const { - return reg->template remove_if_exists(entt); + static_assert(sizeof...(Type) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + return reg->template remove_if_exists(entt); } /** @@ -9651,30 +18547,31 @@ struct basic_handle { * @sa basic_registry::remove_all */ void remove_all() const { + static_assert(sizeof...(Type) == 0, "Invalid operation"); reg->remove_all(entt); } /** * @brief Checks if a handle has all the given components. - * @sa basic_registry::has + * @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 - [[nodiscard]] decltype(auto) has() const { - return reg->template has(entt); + template + [[nodiscard]] decltype(auto) all_of() const { + return reg->template all_of(entt); } /** * @brief Checks if a handle has at least one of the given components. - * @sa basic_registry::any + * @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 - [[nodiscard]] decltype(auto) any() const { - return reg->template any(entt); + template + [[nodiscard]] decltype(auto) any_of() const { + return reg->template any_of(entt); } /** @@ -9683,9 +18580,10 @@ struct basic_handle { * @tparam Component Types of components to get. * @return References to the components owned by the handle. */ - template + template [[nodiscard]] decltype(auto) get() const { - return reg->template get(entt); + static_assert(sizeof...(Type) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + return reg->template get(entt); } /** @@ -9698,6 +18596,7 @@ struct basic_handle { */ template [[nodiscard]] decltype(auto) get_or_emplace(Args &&... args) const { + static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v), "Invalid type"); return reg->template get_or_emplace(entt, std::forward(args)...); } @@ -9707,9 +18606,10 @@ struct basic_handle { * @tparam Component Types of components to get. * @return Pointers to the components owned by the handle. */ - template - [[nodiscard]] decltype(auto) try_get() const { - return reg->template try_get(entt); + template + [[nodiscard]] auto try_get() const { + static_assert(sizeof...(Type) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + return reg->template try_get(entt); } /** @@ -9738,11 +18638,18 @@ private: /** - * @brief Deduction guide. - * @tparam Entity A valid entity type (see entt_traits for more details). + * @brief Compares two handles. + * @tparam Type A valid entity type (see entt_traits for more details). + * @tparam Other A valid entity type (see entt_traits for more details). + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return False if both handles refer to the same registry and the same + * entity, true otherwise. */ -template -basic_handle(basic_registry &, Entity) -> basic_handle; +template +bool operator!=(const basic_handle &lhs, const basic_handle &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} /** @@ -9750,7 +18657,17 @@ basic_handle(basic_registry &, Entity) -> basic_handle; * @tparam Entity A valid entity type (see entt_traits for more details). */ template -basic_handle(const basic_registry &, Entity) -> basic_handle; +basic_handle(basic_registry &, Entity) +-> basic_handle; + + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +basic_handle(const basic_registry &, Entity) +-> basic_handle; } @@ -9769,6 +18686,373 @@ basic_handle(const basic_registry &, Entity) -> basic_handle +#include +#include +#include +#include +// #include "../core/type_traits.hpp" + +// #include "../config/config.h" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +auto function_pointer(Ret(*)(Args...)) -> Ret(*)(Args...); + + +template +auto function_pointer(Ret(*)(Type, Args...), Other &&) -> Ret(*)(Args...); + + +template +auto function_pointer(Ret(Class:: *)(Args...), Other &&...) -> Ret(*)(Args...); + + +template +auto function_pointer(Ret(Class:: *)(Args...) const, Other &&...) -> Ret(*)(Args...); + + +template +auto function_pointer(Type Class:: *, Other &&...) -> Type(*)(); + + +template +using function_pointer_t = decltype(internal::function_pointer(std::declval()...)); + + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret(*)(Args...)) { + return std::index_sequence_for{}; +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/*! @brief Used to wrap a function or a member of a specified type. */ +template +struct connect_arg_t {}; + + +/*! @brief Constant of type connect_arg_t used to disambiguate calls. */ +template +inline constexpr connect_arg_t 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 +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 +class delegate { + template + [[nodiscard]] auto wrap(std::index_sequence) ENTT_NOEXCEPT { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(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 + : fn{nullptr}, data{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 + delegate(connect_arg_t) ENTT_NOEXCEPT { + connect(); + } + + /** + * @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 + delegate(connect_arg_t, Type &&value_or_instance) ENTT_NOEXCEPT { + connect(std::forward(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 + void connect() ENTT_NOEXCEPT { + data = nullptr; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *, Args... args) -> Ret { + return Ret(std::invoke(Candidate, std::forward(args)...)); + }; + } else if constexpr(std::is_member_pointer_v) { + fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); + } else { + fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @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.
+ * 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 + void connect(Type &value_or_instance) ENTT_NOEXCEPT { + data = &value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(payload)); + return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @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 + void connect(Type *value_or_instance) ENTT_NOEXCEPT { + data = value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(payload)); + return Ret(std::invoke(Candidate, curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @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.
+ * 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 { + fn = function; + data = payload; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() ENTT_NOEXCEPT { + fn = nullptr; + data = 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 * instance() const ENTT_NOEXCEPT { + return data; + } + + /** + * @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(*this)); + return fn(data, std::forward(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 test also data + 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 &other) const ENTT_NOEXCEPT { + return fn == other.fn && data == other.data; + } + +private: + function_type *fn; + const void *data; +}; + + +/** + * @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 +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) +-> delegate>>; + + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + */ +template +delegate(connect_arg_t, Type &&) +-> delegate>>; + + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +delegate(Ret(*)(const void *, Args...), const void * = nullptr) +-> delegate; + + +} + + +#endif // #include "registry.hpp" @@ -9781,13 +19065,14 @@ namespace entt { /** * @brief Converts a registry to a view. - * @tparam Const Constness of the accepted registry. * @tparam Entity A valid entity type (see entt_traits for more details). */ -template +template struct as_view { + /*! @brief Underlying entity identifier. */ + using entity_type = std::remove_const_t; /*! @brief Type of registry to convert. */ - using registry_type = std::conditional_t, basic_registry>; + using registry_type = constness_as_t, Entity>; /** * @brief Constructs a converter for a given registry. @@ -9802,7 +19087,7 @@ struct as_view { * @return A newly created view. */ template - operator basic_view() const { + operator basic_view() const { return reg.template view(Exclude{}); } @@ -9813,30 +19098,30 @@ private: /** * @brief Deduction guide. - * - * It allows to deduce the constness of a registry directly from the instance - * provided to the constructor. - * * @tparam Entity A valid entity type (see entt_traits for more details). */ template -as_view(basic_registry &) ENTT_NOEXCEPT -> as_view; +as_view(basic_registry &) -> as_view; -/*! @copydoc as_view */ +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ template -as_view(const basic_registry &) ENTT_NOEXCEPT -> as_view; +as_view(const basic_registry &) -> as_view; /** * @brief Converts a registry to a group. - * @tparam Const Constness of the accepted registry. * @tparam Entity A valid entity type (see entt_traits for more details). */ -template +template struct as_group { + /*! @brief Underlying entity identifier. */ + using entity_type = std::remove_const_t; /*! @brief Type of registry to convert. */ - using registry_type = std::conditional_t, basic_registry>; + using registry_type = constness_as_t, Entity>; /** * @brief Constructs a converter for a given registry. @@ -9852,8 +19137,12 @@ struct as_group { * @return A newly created group. */ template - operator basic_group() const { - return reg.template group(Get{}, Exclude{}); + operator basic_group() const { + if constexpr(std::is_const_v) { + return reg.template group_if_exists(Get{}, Exclude{}); + } else { + return reg.template group(Get{}, Exclude{}); + } } private: @@ -9863,19 +19152,18 @@ private: /** * @brief Deduction guide. - * - * It allows to deduce the constness of a registry directly from the instance - * provided to the constructor. - * * @tparam Entity A valid entity type (see entt_traits for more details). */ template -as_group(basic_registry &) ENTT_NOEXCEPT -> as_group; +as_group(basic_registry &) -> as_group; -/*! @copydoc as_group */ +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ template -as_group(const basic_registry &) ENTT_NOEXCEPT -> as_group; +as_group(const basic_registry &) -> as_group; @@ -9905,7 +19193,8 @@ void invoke(basic_registry ®, const Entity entt) { */ template Entity to_entity(const basic_registry ®, const Component &component) { - return *(reg.template data() + (&component - reg.template raw())); + const auto view = reg.template view(); + return *(view.data() + (&component - view.raw())); } @@ -9928,6 +19217,8 @@ Entity to_entity(const basic_registry ®, const Component &component) // #include "../core/type_traits.hpp" +// #include "../signal/delegate.hpp" + // #include "registry.hpp" // #include "storage.hpp" @@ -10101,20 +19392,20 @@ class basic_observer { template struct matcher_handler, type_list, AnyOf>> { template - static void maybe_valid_if(basic_observer &obs, const basic_registry ®, const Entity entt) { - if(reg.template has(entt) && !reg.template any(entt)) { - if(auto *comp = obs.view.try_get(entt); !comp) { - obs.view.emplace(entt); + static void maybe_valid_if(basic_observer &obs, basic_registry ®, const Entity entt) { + if(reg.template all_of(entt) && !reg.template any_of(entt)) { + if(!obs.storage.contains(entt)) { + obs.storage.emplace(entt); } - obs.view.get(entt) |= (1 << Index); + obs.storage.get(entt) |= (1 << Index); } } template - static void discard_if(basic_observer &obs, const basic_registry &, const Entity entt) { - if(auto *value = obs.view.try_get(entt); value && !(*value &= (~(1 << Index)))) { - obs.view.erase(entt); + static void discard_if(basic_observer &obs, basic_registry &, const Entity entt) { + if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) { + obs.storage.remove(entt); } } @@ -10136,21 +19427,28 @@ class basic_observer { template struct matcher_handler, type_list, type_list, AllOf...>> { - template - static void maybe_valid_if(basic_observer &obs, const basic_registry ®, const Entity entt) { - if(reg.template has(entt) && !reg.template any(entt)) { - if(auto *comp = obs.view.try_get(entt); !comp) { - obs.view.emplace(entt); + template + static void maybe_valid_if(basic_observer &obs, basic_registry ®, const Entity entt) { + if([®, entt]() { + if constexpr(sizeof...(Ignore) == 0) { + return reg.template all_of(entt) && !reg.template any_of(entt); + } else { + return reg.template all_of(entt) && ((std::is_same_v || !reg.template any_of(entt)) && ...) && !reg.template any_of(entt); + } + }()) + { + if(!obs.storage.contains(entt)) { + obs.storage.emplace(entt); } - obs.view.get(entt) |= (1 << Index); + obs.storage.get(entt) |= (1 << Index); } } template - static void discard_if(basic_observer &obs, const basic_registry &, const Entity entt) { - if(auto *value = obs.view.try_get(entt); value && !(*value &= (~(1 << Index)))) { - obs.view.erase(entt); + static void discard_if(basic_observer &obs, basic_registry &, const Entity entt) { + if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) { + obs.storage.remove(entt); } } @@ -10159,7 +19457,7 @@ class basic_observer { (reg.template on_destroy().template connect<&discard_if>(obs), ...); (reg.template on_construct().template connect<&discard_if>(obs), ...); (reg.template on_construct().template connect<&maybe_valid_if>(obs), ...); - (reg.template on_destroy().template connect<&maybe_valid_if>(obs), ...); + (reg.template on_destroy().template connect<&maybe_valid_if>(obs), ...); (reg.template on_destroy().template connect<&discard_if>(obs), ...); (reg.template on_construct().template connect<&discard_if>(obs), ...); } @@ -10175,7 +19473,7 @@ class basic_observer { }; template - static void disconnect(basic_observer &obs, basic_registry ®) { + static void disconnect(basic_registry ®, basic_observer &obs) { (matcher_handler::disconnect(obs, reg), ...); } @@ -10183,7 +19481,7 @@ class basic_observer { void connect(basic_registry ®, std::index_sequence) { static_assert(sizeof...(Matcher) < std::numeric_limits::digits, "Too many matchers"); (matcher_handler::template connect(*this, reg), ...); - release = &basic_observer::disconnect; + release.template connect<&basic_observer::disconnect>(reg); } public: @@ -10192,11 +19490,12 @@ public: /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Random access iterator type. */ - using iterator = typename sparse_set::iterator; + using iterator = typename basic_sparse_set::iterator; /*! @brief Default constructor. */ basic_observer() - : target{}, release{}, view{} + : release{}, + storage{} {} /*! @brief Default copy constructor, deleted on purpose. */ @@ -10211,9 +19510,7 @@ public: */ template basic_observer(basic_registry ®, basic_collector) - : target{®}, - release{}, - view{} + : basic_observer{} { connect(reg, std::index_sequence_for{}); } @@ -10242,15 +19539,14 @@ public: void connect(basic_registry ®, basic_collector) { disconnect(); connect(reg, std::index_sequence_for{}); - target = ® - view.clear(); + storage.clear(); } /*! @brief Disconnects an observer from the registry it keeps track of. */ void disconnect() { if(release) { - release(*this, *target); - release = nullptr; + release(*this); + release.reset(); } } @@ -10259,7 +19555,7 @@ public: * @return Number of elements. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { - return view.size(); + return storage.size(); } /** @@ -10267,13 +19563,13 @@ public: * @return True if the observer is empty, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { - return view.empty(); + 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 + * The returned pointer is such that range `[data(), data() + size())` is * always a valid range, even if the container is empty. * * @note @@ -10283,7 +19579,7 @@ public: * @return A pointer to the array of entities. */ [[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT { - return view.data(); + return storage.data(); } /** @@ -10295,7 +19591,7 @@ public: * @return An iterator to the first entity of the observer. */ [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { - return view.sparse_set::begin(); + return storage.basic_sparse_set::begin(); } /** @@ -10309,12 +19605,12 @@ public: * observer. */ [[nodiscard]] iterator end() const ENTT_NOEXCEPT { - return view.sparse_set::end(); + return storage.basic_sparse_set::end(); } /*! @brief Clears the underlying container. */ void clear() ENTT_NOEXCEPT { - view.clear(); + storage.clear(); } /** @@ -10353,9 +19649,8 @@ public: } private: - basic_registry *target; - void(* release)(basic_observer &, basic_registry &); - storage view; + delegate release; + basic_storage storage; }; @@ -10364,11 +19659,2612 @@ private: #endif -// #include "entity/pool.hpp" +// #include "entity/organizer.hpp" +#ifndef ENTT_ENTITY_ORGANIZER_HPP +#define ENTT_ENTITY_ORGANIZER_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + +// #include "helper.hpp" +#ifndef ENTT_ENTITY_HELPER_HPP +#define ENTT_ENTITY_HELPER_HPP + + +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "../signal/delegate.hpp" + +// #include "registry.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Converts a registry to a view. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +struct as_view { + /*! @brief Underlying entity identifier. */ + using entity_type = std::remove_const_t; + /*! @brief Type of registry to convert. */ + using registry_type = constness_as_t, 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 + operator basic_view() const { + return reg.template view(Exclude{}); + } + +private: + registry_type ® +}; + + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +as_view(basic_registry &) -> as_view; + + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +as_view(const basic_registry &) -> as_view; + + +/** + * @brief Converts a registry to a group. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +struct as_group { + /*! @brief Underlying entity identifier. */ + using entity_type = std::remove_const_t; + /*! @brief Type of registry to convert. */ + using registry_type = constness_as_t, Entity>; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_group(registry_type &source) ENTT_NOEXCEPT: reg{source} {} + + /** + * @brief Conversion function from a registry to a group. + * @tparam Exclude Types of components used to filter the group. + * @tparam Get Types of components observed by the group. + * @tparam Owned Types of components owned by the group. + * @return A newly created group. + */ + template + operator basic_group() const { + if constexpr(std::is_const_v) { + return reg.template group_if_exists(Get{}, Exclude{}); + } else { + return reg.template group(Get{}, Exclude{}); + } + } + +private: + registry_type ® +}; + + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +as_group(basic_registry &) -> as_group; + + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +as_group(const basic_registry &) -> as_group; + + + +/** + * @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 +void invoke(basic_registry ®, const Entity entt) { + static_assert(std::is_member_function_pointer_v, "Invalid pointer to non-static member function"); + delegate &, const Entity)> func; + func.template connect(reg.template get>(entt)); + func(reg, entt); +} + + +/** + * @brief Returns the entity associated with a given component. + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Type of component. + * @param reg A registry that contains the given entity and its components. + * @param component A valid component instance. + * @return The entity associated with the given component. + */ +template +Entity to_entity(const basic_registry ®, const Component &component) { + const auto view = reg.template view(); + return *(view.data() + (&component - view.raw())); +} + + +} + + +#endif + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +struct is_view: std::false_type {}; + +template +struct is_view, Component...>>: std::true_type {}; + +template +inline constexpr bool is_view_v = is_view::value; + + +template +struct unpack_type { + using ro = std::conditional_t< + type_list_contains_v> || (std::is_const_v && !type_list_contains_v>), + type_list>, + type_list<> + >; + + using rw = std::conditional_t< + type_list_contains_v> || (!std::is_const_v && !type_list_contains_v>), + type_list, + type_list<> + >; +}; + +template +struct unpack_type, type_list> { + using ro = type_list<>; + using rw = type_list<>; +}; + +template +struct unpack_type, type_list> + : unpack_type, type_list> +{}; + +template +struct unpack_type, Component...>, type_list> { + using ro = type_list_cat_t, typename unpack_type>::ro...>; + using rw = type_list_cat_t>::rw...>; +}; + +template +struct unpack_type, Component...>, type_list> + : unpack_type, Component...>, type_list> +{}; + + +template +struct resource; + +template +struct resource, type_list> { + using args = type_list...>; + using ro = type_list_cat_t>::ro..., typename unpack_type>::ro...>; + using rw = type_list_cat_t>::rw..., typename unpack_type>::rw...>; +}; + + +template +resource...>, type_list> free_function_to_resource(Ret(*)(Args...)); + +template +resource...>, type_list> constrained_function_to_resource(Ret(*)(Type &, Args...)); + +template +resource...>, type_list> constrained_function_to_resource(Ret(Class:: *)(Args...)); + +template +resource...>, type_list> constrained_function_to_resource(Ret(Class:: *)(Args...) const); + +template +resource, type_list> to_resource(); + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Utility class for creating a static task graph. + * + * This class offers minimal support (but sufficient in many cases) for creating + * an execution graph from functions and their requirements on resources.
+ * 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 +class basic_organizer final { + using callback_type = void(const void *, entt::basic_registry &); + using prepare_type = void(entt::basic_registry &); + using dependency_type = std::size_t(const bool, type_info *, const std::size_t); + + struct vertex_data final { + std::size_t ro_count{}; + std::size_t rw_count{}; + const char *name{}; + const void *payload{}; + callback_type *callback{}; + dependency_type *dependency; + prepare_type *prepare{}; + type_info info{}; + }; + + template + [[nodiscard]] static decltype(auto) extract(basic_registry ®) { + if constexpr(std::is_same_v>) { + return reg; + } else if constexpr(internal::is_view_v) { + return as_view{reg}; + } else { + return reg.template ctx_or_set>(); + } + } + + template + [[nodiscard]] static auto to_args(basic_registry ®, type_list) { + return std::tuple(reg))...>(extract(reg)...); + } + + template + static std::size_t fill_dependencies(type_list, [[maybe_unused]] type_info *buffer, [[maybe_unused]] const std::size_t count) { + if constexpr(sizeof...(Type) == 0u) { + return {}; + } else { + type_info info[sizeof...(Type)]{type_id()...}; + const auto length = std::min(count, sizeof...(Type)); + std::copy_n(info, length, buffer); + return length; + } + } + + template + void track_dependencies(std::size_t index, const bool requires_registry, type_list, type_list) { + dependencies[type_hash>::value()].emplace_back(index, requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u)); + (dependencies[type_hash::value()].emplace_back(index, false), ...); + (dependencies[type_hash::value()].emplace_back(index, true), ...); + } + + [[nodiscard]] std::vector adjacency_matrix() { + const auto length = vertices.size(); + std::vector edges(length * length, false); + + // creates the ajacency matrix + for(const auto &deps: dependencies) { + const auto last = deps.second.cend(); + auto it = deps.second.cbegin(); + + while(it != last) { + if(it->second) { + // rw item + if(auto curr = it++; it != last) { + if(it->second) { + edges[curr->first * length + it->first] = true; + } else { + if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) { + for(; it != next; ++it) { + edges[curr->first * length + it->first] = true; + edges[it->first * length + next->first] = true; + } + } else { + for(; it != next; ++it) { + edges[curr->first * length + it->first] = true; + } + } + } + } + } else { + // ro item, possibly only on first iteration + if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) { + for(; it != next; ++it) { + edges[it->first * length + next->first] = true; + } + } else { + it = last; + } + } + } + } + + // computes the transitive closure + for(std::size_t vk{}; vk < length; ++vk) { + for(std::size_t vi{}; vi < length; ++vi) { + for(std::size_t vj{}; vj < length; ++vj) { + edges[vi * length + vj] = edges[vi * length + vj] || (edges[vi * length + vk] && edges[vk * length + vj]); + } + } + } + + // applies the transitive reduction + for(std::size_t vert{}; vert < length; ++vert) { + edges[vert * length + vert] = false; + } + + for(std::size_t vj{}; vj < length; ++vj) { + for(std::size_t vi{}; vi < length; ++vi) { + if(edges[vi * length + vj]) { + for(std::size_t vk{}; vk < length; ++vk) { + if(edges[vj * length + vk]) { + edges[vi * length + vk] = false; + } + } + } + } + } + + return edges; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Raw task function type. */ + using function_type = callback_type; + + /*! @brief Vertex type of a task graph defined as an adjacency list. */ + struct vertex { + /** + * @brief Constructs a vertex of the task graph. + * @param vtype True if the vertex is a top-level one, false otherwise. + * @param data The data associated with the vertex. + * @param edges The indices of the children in the adjacency list. + */ + vertex(const bool vtype, vertex_data data, std::vector edges) + : is_top_level{vtype}, + node{std::move(data)}, + reachable{std::move(edges)} + {} + + /** + * @brief Fills a buffer with the type info objects for the writable + * resources of a vertex. + * @param buffer A buffer pre-allocated by the user. + * @param length The length of the user-supplied buffer. + * @return The number of type info objects written to the buffer. + */ + size_type ro_dependency(type_info *buffer, const std::size_t length) const ENTT_NOEXCEPT { + return node.dependency(false, buffer, length); + } + + /** + * @brief Fills a buffer with the type info objects for the read-only + * resources of a vertex. + * @param buffer A buffer pre-allocated by the user. + * @param length The length of the user-supplied buffer. + * @return The number of type info objects written to the buffer. + */ + size_type rw_dependency(type_info *buffer, const std::size_t length) const ENTT_NOEXCEPT { + return node.dependency(true, buffer, length); + } + + /** + * @brief Returns the number of read-only resources of a vertex. + * @return The number of read-only resources of the vertex. + */ + size_type ro_count() const ENTT_NOEXCEPT { + return node.ro_count; + } + + /** + * @brief Returns the number of writable resources of a vertex. + * @return The number of writable resources of the vertex. + */ + size_type rw_count() const ENTT_NOEXCEPT { + return node.rw_count; + } + + /** + * @brief Checks if a vertex is also a top-level one. + * @return True if the vertex is a top-level one, false otherwise. + */ + bool top_level() const ENTT_NOEXCEPT { + return is_top_level; + } + + /** + * @brief Returns a type info object associated with a vertex. + * @return A properly initialized type info object. + */ + type_info info() const ENTT_NOEXCEPT { + return node.info; + } + + /** + * @brief Returns a user defined name associated with a vertex, if any. + * @return The user defined name associated with the vertex, if any. + */ + const char * name() const ENTT_NOEXCEPT { + return node.name; + } + + /** + * @brief Returns the function associated with a vertex. + * @return The function associated with the vertex. + */ + function_type * callback() const ENTT_NOEXCEPT { + return node.callback; + } + + /** + * @brief Returns the payload associated with a vertex, if any. + * @return The payload associated with the vertex, if any. + */ + const void * data() const ENTT_NOEXCEPT { + return node.payload; + } + + /** + * @brief Returns the list of nodes reachable from a given vertex. + * @return The list of nodes reachable from the vertex. + */ + const std::vector & 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 ®) const { + node.prepare ? node.prepare(reg) : void(); + } + + private: + bool is_top_level; + vertex_data node; + std::vector 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 + void emplace(const char *name = nullptr) { + using resource_type = decltype(internal::free_function_to_resource(Candidate)); + constexpr auto requires_registry = type_list_contains_v>; + + callback_type *callback = +[](const void *, basic_registry ®) { + std::apply(Candidate, to_args(reg, typename resource_type::args{})); + }; + + track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{}); + + vertices.push_back({ + resource_type::ro::size, + resource_type::rw::size, + name, + nullptr, + callback, + +[](const bool rw, type_info *buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, + +[](basic_registry ®) { void(to_args(reg, typename resource_type::args{})); }, + type_id>() + }); + } + + /** + * @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 + void emplace(Type &value_or_instance, const char *name = nullptr) { + using resource_type = decltype(internal::constrained_function_to_resource(Candidate)); + constexpr auto requires_registry = type_list_contains_v>; + + callback_type *callback = +[](const void *payload, basic_registry ®) { + Type *curr = static_cast(const_cast *>(payload)); + std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{}))); + }; + + track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{}); + + vertices.push_back({ + resource_type::ro::size, + resource_type::rw::size, + name, + &value_or_instance, + callback, + +[](const bool rw, type_info *buffer, const std::size_t length) { + return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); + }, + +[](basic_registry ®) { + void(to_args(reg, typename resource_type::args{})); + }, + type_id>() + }); + } + + /** + * @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 + void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) { + using resource_type = internal::resource, type_list>; + track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{}); + + vertices.push_back({ + resource_type::ro::size, + resource_type::rw::size, + name, + payload, + func, + +[](const bool rw, type_info *buffer, const std::size_t length) { + return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); + }, + nullptr, + type_info{} + }); + } + + /** + * @brief Generates a task graph for the current content. + * @return The adjacency list of the task graph. + */ + std::vector graph() { + const auto edges = adjacency_matrix(); + + // creates the adjacency list + std::vector adjacency_list{}; + adjacency_list.reserve(vertices.size()); + + for(std::size_t col{}, length = vertices.size(); col < length; ++col) { + std::vector reachable{}; + const auto row = col * length; + bool is_top_level = true; + + for(std::size_t next{}; next < length; ++next) { + if(edges[row + next]) { + reachable.push_back(next); + } + } + + for(std::size_t next{}; next < length && is_top_level; ++next) { + is_top_level = !edges[next * length + col]; + } + + adjacency_list.emplace_back(is_top_level, vertices[col], std::move(reachable)); + } + + return adjacency_list; + } + + /*! @brief Erases all elements from a container. */ + void clear() { + dependencies.clear(); + vertices.clear(); + } + +private: + std::unordered_map>> dependencies; + std::vector vertices; +}; + + +} + + +#endif + +// #include "entity/poly_storage.hpp" +#ifndef ENTT_ENTITY_POLY_STORAGE_HPP +#define ENTT_ENTITY_POLY_STORAGE_HPP + + +#include +#include +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../poly/poly.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Basic poly storage implementation. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +struct Storage: type_list { + /** + * @brief Concept definition. + * @tparam Base Opaque base class from which to inherit. + */ + template + struct type: Base { + /** + * @brief Returns a type info for the contained objects. + * @return The type info for the contained objects. + */ + type_info value_type() const ENTT_NOEXCEPT { + return poly_call<0>(*this); + } + }; + + /** + * @brief Concept implementation. + * @tparam Type Type for which to generate an implementation. + */ + template + using impl = value_list<&type_id>; +}; + + +/** + * @brief Defines the poly storage type associate with a given entity type. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +struct poly_storage_traits { + /*! @brief Poly storage type for the given entity type. */ + using storage_type = poly>; +}; + + +} + + +#endif // #include "entity/registry.hpp" +#ifndef ENTT_ENTITY_REGISTRY_HPP +#define ENTT_ENTITY_REGISTRY_HPP + + +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/algorithm.hpp" + +// #include "../core/any.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "group.hpp" + +// #include "poly_storage.hpp" + +// #include "runtime_view.hpp" + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + +// #include "utility.hpp" + +// #include "view.hpp" + + + +namespace entt { + + +/** + * @brief Fast and reliable entity-component system. + * + * The registry is the core class of the entity-component framework.
+ * It stores entities and arranges pools of components on a per request basis. + * By means of a registry, users can manage entities and components, then create + * views or groups to iterate them. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_registry { + using traits_type = entt_traits; + using poly_storage_type = typename poly_storage_traits::storage_type; + + template + using storage_type = constness_as_t>::storage_type, Component>; + + struct pool_data { + poly_storage_type poly; + std::unique_ptr> pool{}; + }; + + template + struct group_handler; + + template + struct group_handler, get_t, Owned...> { + static_assert(std::conjunction_v>..., std::is_same>..., std::is_same>...>, "One or more component types are invalid"); + std::conditional_t, std::size_t> current{}; + + template + void maybe_valid_if(basic_registry &owner, const Entity entt) { + [[maybe_unused]] const auto cpools = std::make_tuple(owner.assure()...); + + const auto is_valid = ((std::is_same_v || std::get *>(cpools)->contains(entt)) && ...) + && ((std::is_same_v || owner.assure()->contains(entt)) && ...) + && ((std::is_same_v || !owner.assure()->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 *>(cpools)->swap(std::get *>(cpools)->data()[pos], entt), ...); + } + } + } + + void discard_if([[maybe_unused]] basic_registry &owner, const Entity entt) { + if constexpr(sizeof...(Owned) == 0) { + if(current.contains(entt)) { + current.remove(entt); + } + } else { + if(const auto cpools = std::make_tuple(owner.assure()...); std::get<0>(cpools)->contains(entt) && (std::get<0>(cpools)->index(entt) < current)) { + const auto pos = --current; + (std::get *>(cpools)->swap(std::get *>(cpools)->data()[pos], entt), ...); + } + } + } + }; + + struct group_data { + std::size_t size; + std::unique_ptr group; + bool (* owned)(const id_type) ENTT_NOEXCEPT; + bool (* get)(const id_type) ENTT_NOEXCEPT; + bool (* exclude)(const id_type) ENTT_NOEXCEPT; + }; + + template + [[nodiscard]] storage_type * assure() const { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); + const auto index = type_seq::value(); + + if(!(index < pools.size())) { + pools.resize(size_type(index)+1u); + } + + if(auto &&pdata = pools[index]; !pdata.pool) { + pdata.pool.reset(new storage_type()); + pdata.poly.template emplace &>(*static_cast *>(pdata.pool.get())); + } + + return static_cast *>(pools[index].pool.get()); + } + + template + [[nodiscard]] const storage_type * pool_if_exists() const { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); + const auto index = type_seq::value(); + return (!(index < pools.size()) || !pools[index].pool) ? nullptr : static_cast *>(pools[index].pool.get()); + } + + Entity generate_identifier() { + // traits_type::entity_mask is reserved to allow for null identifiers + ENTT_ASSERT(static_cast(entities.size()) < traits_type::entity_mask); + return entities.emplace_back(entity_type{static_cast(entities.size())}); + } + + Entity recycle_identifier() { + ENTT_ASSERT(available != null); + const auto curr = to_integral(available); + const auto version = to_integral(entities[curr]) & (traits_type::version_mask << traits_type::entity_shift); + available = entity_type{to_integral(entities[curr]) & traits_type::entity_mask}; + return entities[curr] = entity_type{curr | version}; + } + + void release_entity(const Entity entity, const typename traits_type::version_type version) { + const auto entt = to_integral(entity) & traits_type::entity_mask; + entities[entt] = entity_type{to_integral(available) | (typename traits_type::entity_type{version} << traits_type::entity_shift)}; + available = entity_type{entt}; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Underlying version type. */ + using version_type = typename traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Poly storage type. */ + using poly_storage = typename poly_storage_traits::storage_type; + + /** + * @brief Returns the entity identifier without the version. + * @param entity An entity identifier, either valid or not. + * @return The entity identifier without the version. + */ + [[nodiscard]] static entity_type entity(const entity_type entity) ENTT_NOEXCEPT { + return entity_type{to_integral(entity) & traits_type::entity_mask}; + } + + /** + * @brief Returns the version stored along with an entity identifier. + * @param entity An entity identifier, either valid or not. + * @return The version stored along with the given entity identifier. + */ + [[nodiscard]] static version_type version(const entity_type entity) ENTT_NOEXCEPT { + return version_type(to_integral(entity) >> traits_type::entity_shift); + } + + /*! @brief Default constructor. */ + basic_registry() = default; + + /*! @brief Default move constructor. */ + basic_registry(basic_registry &&) = default; + + /*! @brief Default move assignment operator. @return This registry. */ + basic_registry & operator=(basic_registry &&) = default; + + /** + * @brief Prepares a pool for the given type if required. + * @tparam Component Type of component for which to prepare a pool. + */ + template + void prepare() { + // suppress the warning due to the [[nodiscard]] attribute + static_cast(assure()); + } + + /** + * @brief Returns a poly storage for a given type. + * @param info The type for which to return a poly storage. + * @return A valid poly storage if a pool for the given type exists, an + * empty and thus invalid element otherwise. + */ + poly_storage & storage(const type_info info) { + ENTT_ASSERT(info.seq() < pools.size() && pools[info.seq()].poly); + return pools[info.seq()].poly; + } + + /*! @copydoc storage */ + const poly_storage & storage(const type_info info) const { + ENTT_ASSERT(info.seq() < pools.size() && pools[info.seq()].poly); + return pools[info.seq()].poly; + } + + /** + * @brief Returns the number of existing components of the given type. + * @tparam Component Type of component of which to return the size. + * @return Number of existing components of the given type. + */ + template + [[nodiscard]] size_type size() const { + const auto *cpool = pool_if_exists(); + return cpool ? cpool->size() : size_type{}; + } + + /** + * @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 = available; curr != null; --sz) { + curr = entities[to_integral(curr) & traits_type::entity_mask]; + } + + return sz; + } + + /** + * @brief Increases the capacity of the registry or of the pools for the + * given components. + * + * If no components are specified, the capacity of the registry is + * increased, that is the number of entities it contains. Otherwise the + * capacity of the pools for the given components is increased.
+ * In both cases, if the new capacity is greater than the current capacity, + * new storage is allocated, otherwise the method does nothing. + * + * @tparam Component Types of components for which to reserve storage. + * @param cap Desired capacity. + */ + template + void reserve(const size_type cap) { + if constexpr(sizeof...(Component) == 0) { + entities.reserve(cap); + } else { + (assure()->reserve(cap), ...); + } + } + + /** + * @brief Reserves enough space to store `count` pools. + * @param count Number of pools to reserve space for. + */ + void reserve_pools(const size_t count) { + pools.reserve(count); + } + + /** + * @brief Returns the capacity of the pool for the given component. + * @tparam Component Type of component in which one is interested. + * @return Capacity of the pool of the given component. + */ + template + [[nodiscard]] size_type capacity() const { + const auto *cpool = pool_if_exists(); + return cpool ? cpool->capacity() : size_type{}; + } + + /** + * @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 Requests the removal of unused capacity for the given components. + * @tparam Component Types of components for which to reclaim unused + * capacity. + */ + template + void shrink_to_fit() { + (assure()->shrink_to_fit(), ...); + } + + /** + * @brief Checks whether the registry or the pools of the given components + * are empty. + * + * A registry is considered empty when it doesn't contain entities that are + * still in use. + * + * @tparam Component Types of components in which one is interested. + * @return True if the registry or the pools of the given components are + * empty, false otherwise. + */ + template + [[nodiscard]] bool empty() const { + if constexpr(sizeof...(Component) == 0) { + return !alive(); + } else { + return [](const auto *... cpool) { return ((!cpool || cpool->empty()) && ...); }(pool_if_exists()...); + } + } + + /** + * @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 container 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 destroyed entities. + * + * This function is intended for use in conjunction with `assign`.
+ * The returned entity has an invalid identifier in all cases. + * + * @return The head of the list of destroyed entities. + */ + [[nodiscard]] entity_type destroyed() const ENTT_NOEXCEPT { + return available; + } + + /** + * @brief Checks if an entity identifier refers to a valid entity. + * @param entity An entity 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(to_integral(entity) & traits_type::entity_mask); + return (pos < entities.size() && entities[pos] == entity); + } + + /** + * @brief Returns the actual version for an entity identifier. + * + * @warning + * Attempting to use an entity that doesn't belong to the registry results + * in undefined behavior. An entity belongs to the registry even if it has + * been previously destroyed and/or recycled. + * + * @param entity A valid entity identifier. + * @return Actual version for the given entity identifier. + */ + [[nodiscard]] version_type current(const entity_type entity) const { + const auto pos = size_type(to_integral(entity) & traits_type::entity_mask); + ENTT_ASSERT(pos < entities.size()); + return version(entities[pos]); + } + + /** + * @brief Creates a new entity and returns it. + * + * There are two kinds of possible entity identifiers: + * + * * Newly created ones in case no entities have been previously destroyed. + * * Recycled ones with updated versions. + * + * @return A valid entity identifier. + */ + entity_type create() { + return available == null ? generate_identifier() : recycle_identifier(); + } + + /** + * @brief Creates a new entity and returns it. + * + * @sa create + * + * If the requested entity isn't in use, the suggested identifier is created + * and returned. Otherwise, a new one will be generated for this purpose. + * + * @param hint A desired entity identifier. + * @return A valid entity identifier. + */ + [[nodiscard]] entity_type create(const entity_type hint) { + ENTT_ASSERT(hint != null); + entity_type entt; + + if(const auto req = (to_integral(hint) & traits_type::entity_mask); !(req < entities.size())) { + entities.reserve(size_type(req) + 1u); + + for(auto pos = entities.size(); pos < req; ++pos) { + release_entity(generate_identifier(), {}); + } + + entt = entities.emplace_back(hint); + } else if(const auto curr = (to_integral(entities[req]) & traits_type::entity_mask); req == curr) { + entt = create(); + } else { + auto *it = &available; + for(; (to_integral(*it) & traits_type::entity_mask) != req; it = &entities[to_integral(*it) & traits_type::entity_mask]); + *it = entity_type{curr | (to_integral(*it) & (traits_type::version_mask << traits_type::entity_shift))}; + entt = entities[req] = hint; + } + + return entt; + } + + /** + * @brief Assigns each element in a range an entity. + * + * @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 + void create(It first, It last) { + for(; available != null && first != last; ++first) { + *first = recycle_identifier(); + } + + for(; first != last; ++first) { + *first = generate_identifier(); + } + } + + /** + * @brief Assigns entities to an empty registry. + * + * This function is intended for use in conjunction with `data`, `size` and + * `destroyed`.
+ * 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 + void assign(It first, It last, const entity_type destroyed) { + ENTT_ASSERT(!alive()); + entities.assign(first, last); + available = destroyed; + } + + /** + * @brief Destroys an entity. + * + * When an entity is destroyed, its version is updated and the identifier + * can be recycled at any time. + * + * @sa remove_all + * + * @param entity A valid entity identifier. + */ + void destroy(const entity_type entity) { + destroy(entity, static_cast(version(entity) + 1u)); + } + + /** + * @brief Destroys an entity. + * + * If the entity isn't already destroyed, the suggested version is used + * instead of the implicitly generated one. + * + * @sa remove_all + * + * @param entity A valid entity identifier. + * @param version A desired version upon destruction. + */ + void destroy(const entity_type entity, const version_type version) { + remove_all(entity); + release_entity(entity, version); + } + + /** + * @brief Destroys all the entities in a range. + * + * @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 + void destroy(It first, It last) { + for(; first != last; ++first) { + destroy(*first); + } + } + + /** + * @brief Assigns the given component to an entity. + * + * A new instance of the given component is created and initialized with the + * arguments provided (the component must have a proper constructor or be of + * aggregate type). Then the component is assigned to the given entity. + * + * @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 entity identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace(const entity_type entity, Args &&... args) { + ENTT_ASSERT(valid(entity)); + return assure()->emplace(*this, entity, std::forward(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 + void insert(It first, It last, const Component &value = {}) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); })); + assure()->insert(*this, 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. + * @param to An iterator past the last element of the range of components. + */ + template + void insert(EIt first, EIt last, CIt from, CIt to) { + static_assert(std::is_constructible_v::value_type>, "Invalid value type"); + ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); })); + assure()->insert(*this, first, last, from, to); + } + + /** + * @brief Assigns or replaces the given component for an entity. + * + * Equivalent to the following snippet (pseudocode): + * + * @code{.cpp} + * auto &component = registry.all_of(entity) ? registry.replace(entity, args...) : registry.emplace(entity, args...); + * @endcode + * + * Prefer this function anyway because it has slightly better performance. + * + * @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 entity identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace_or_replace(const entity_type entity, Args &&... args) { + ENTT_ASSERT(valid(entity)); + auto *cpool = assure(); + + return cpool->contains(entity) + ? cpool->patch(*this, entity, [&args...](auto &... curr) { ((curr = Component{std::forward(args)...}), ...); }) + : cpool->emplace(*this, entity, std::forward(args)...); + } + + /** + * @brief Patches the given component for an entity. + * + * The signature of the functions 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 entity identifier. + * @param func Valid function objects. + * @return A reference to the patched component. + */ + template + decltype(auto) patch(const entity_type entity, Func &&... func) { + ENTT_ASSERT(valid(entity)); + return assure()->patch(*this, entity, std::forward(func)...); + } + + /** + * @brief Replaces the given component for an entity. + * + * A new instance of the given component is created and initialized with the + * arguments provided (the component must have a proper constructor or be of + * aggregate type). Then the component is assigned to the given entity. + * + * @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 entity identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the component being replaced. + */ + template + decltype(auto) replace(const entity_type entity, Args &&... args) { + return assure()->patch(*this, entity, [&args...](auto &... curr) { ((curr = Component{std::forward(args)...}), ...); }); + } + + /** + * @brief Removes the given components from an entity. + * + * @warning + * Attempting to use an invalid entity or to remove a component from an + * entity that doesn't own it results in undefined behavior. + * + * @tparam Component Types of components to remove. + * @param entity A valid entity identifier. + */ + template + void remove(const entity_type entity) { + ENTT_ASSERT(valid(entity)); + static_assert(sizeof...(Component) > 0, "Provide one or more component types"); + (assure()->remove(entity, this), ...); + } + + /** + * @brief Removes the given components from all the entities in a range. + * + * @sa remove + * + * @tparam Component 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. + */ + template + void remove(It first, It last) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); })); + static_assert(sizeof...(Component) > 0, "Provide one or more component types"); + (assure()->remove(first, last, this), ...); + } + + /** + * @brief Removes the given components from an entity. + * + * Equivalent to the following snippet (pseudocode): + * + * @code{.cpp} + * if(registry.all_of(entity)) { registry.remove(entity) } + * @endcode + * + * Prefer this function anyway because it has slightly better performance. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Types of components to remove. + * @param entity A valid entity identifier. + * @return The number of components actually removed. + */ + template + size_type remove_if_exists(const entity_type entity) { + ENTT_ASSERT(valid(entity)); + + return ([this, entity](auto *cpool) { + return cpool->contains(entity) ? (cpool->remove(entity, this), true) : false; + }(assure()) + ... + size_type{}); + } + + /** + * @brief Removes all the components from an entity and makes it orphaned. + * + * @warning + * In case there are listeners that observe the destruction of components + * and assign other components to the entity in their bodies, the result of + * invoking this function may not be as expected. In the worst case, it + * could lead to undefined behavior. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @param entity A valid entity identifier. + */ + void remove_all(const entity_type entity) { + ENTT_ASSERT(valid(entity)); + entity_type wrap[1u]{entity}; + + for(auto pos = pools.size(); pos; --pos) { + if(auto &pdata = pools[pos-1]; pdata.pool && pdata.pool->contains(entity)) { + pdata.pool->remove(std::begin(wrap), std::end(wrap), this); + } + } + } + + /** + * @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 entity identifier. + * @return True if the entity has all the components, false otherwise. + */ + template + [[nodiscard]] bool all_of(const entity_type entity) const { + ENTT_ASSERT(valid(entity)); + return [entity](const auto *... cpool) { return ((cpool && cpool->contains(entity)) && ...); }(pool_if_exists()...); + } + + /** + * @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 entity identifier. + * @return True if the entity has at least one of the given components, + * false otherwise. + */ + template + [[nodiscard]] bool any_of(const entity_type entity) const { + ENTT_ASSERT(valid(entity)); + return [entity](const auto *... cpool) { return !((!cpool || !cpool->contains(entity)) && ...); }(pool_if_exists()...); + } + + /** + * @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 entity identifier. + * @return References to the components owned by the entity. + */ + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) const { + ENTT_ASSERT(valid(entity)); + + if constexpr(sizeof...(Component) == 1) { + const auto *cpool = pool_if_exists...>(); + ENTT_ASSERT(cpool); + return cpool->get(entity); + } else { + return std::forward_as_tuple(get(entity)...); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) { + ENTT_ASSERT(valid(entity)); + + if constexpr(sizeof...(Component) == 1) { + return (const_cast(assure>()->get(entity)), ...); + } else { + return std::forward_as_tuple(get(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.
+ * Equivalent to the following snippet (pseudocode): + * + * @code{.cpp} + * auto &component = registry.all_of(entity) ? registry.get(entity) : registry.emplace(entity, args...); + * @endcode + * + * Prefer this function anyway because it has slightly better performance. + * + * @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 entity identifier. + * @param args Parameters to use to initialize the component. + * @return Reference to the component owned by the entity. + */ + template + [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entity, Args &&... args) { + ENTT_ASSERT(valid(entity)); + auto *cpool = assure(); + return cpool->contains(entity) ? cpool->get(entity) : cpool->emplace(*this, entity, std::forward(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 entity identifier. + * @return Pointers to the components owned by the entity. + */ + template + [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) const { + ENTT_ASSERT(valid(entity)); + + if constexpr(sizeof...(Component) == 1) { + const auto *cpool = pool_if_exists...>(); + return (cpool && cpool->contains(entity)) ? &cpool->get(entity) : nullptr; + } else { + return std::make_tuple(try_get(entity)...); + } + } + + /*! @copydoc try_get */ + template + [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) { + ENTT_ASSERT(valid(entity)); + + if constexpr(sizeof...(Component) == 1) { + return (const_cast(std::as_const(*this).template try_get(entity)), ...); + } else { + return std::make_tuple(try_get(entity)...); + } + } + + /** + * @brief Clears a whole registry or the pools for the given components. + * @tparam Component Types of components to remove from their entities. + */ + template + void clear() { + if constexpr(sizeof...(Component) == 0) { + for(auto pos = pools.size(); pos; --pos) { + if(auto &pdata = pools[pos-1]; pdata.pool) { + pdata.pool->clear(this); + } + } + + each([this](const auto entity) { release_entity(entity, version(entity) + 1u); }); + } else { + ([this](auto *cpool) { + cpool->remove(cpool->basic_sparse_set::begin(), cpool->basic_sparse_set::end(), this); + }(assure()), ...); + } + } + + /** + * @brief Iterates all the entities that are still in use. + * + * The function object is invoked for each entity that is still in use.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const Entity); + * @endcode + * + * This function is fairly slow and should not be used frequently. However, + * it's useful for iterating all the entities still in use, regardless of + * their components. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + if(available == 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]; (to_integral(entity) & traits_type::entity_mask) == (pos - 1)) { + func(entity); + } + } + } + } + + /** + * @brief Checks if an entity has components assigned. + * @param entity A valid entity identifier. + * @return True if the entity has no components assigned, false otherwise. + */ + [[nodiscard]] bool orphan(const entity_type entity) const { + ENTT_ASSERT(valid(entity)); + return std::none_of(pools.cbegin(), pools.cend(), [entity](auto &&pdata) { return pdata.pool && pdata.pool->contains(entity); }); + } + + /** + * @brief Iterates orphans and applies them the given function object. + * + * The function object is invoked for each entity that is still in use and + * has no components assigned.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const Entity); + * @endcode + * + * This function can be very slow and should not be used frequently. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void orphans(Func func) const { + each([this, &func](const auto entity) { + if(orphan(entity)) { + func(entity); + } + }); + } + + /** + * @brief Returns a sink object for the given component. + * + * The sink returned by this function can be used to receive notifications + * whenever a new instance of the given component is created and assigned to + * an entity.
+ * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, Entity); + * @endcode + * + * Listeners are invoked **after** the component has been assigned to the + * entity. + * + * @sa sink + * + * @tparam Component Type of component of which to get the sink. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_construct() { + return assure()->on_construct(); + } + + /** + * @brief Returns a sink object for the given component. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance of the given component is explicitly updated.
+ * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, Entity); + * @endcode + * + * Listeners are invoked **after** the component has been updated. + * + * @sa sink + * + * @tparam Component Type of component of which to get the sink. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_update() { + return assure()->on_update(); + } + + /** + * @brief Returns a sink object for the given component. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance of the given component is removed from an entity and + * thus destroyed.
+ * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, Entity); + * @endcode + * + * Listeners are invoked **before** the component has been removed from the + * entity. + * + * @sa sink + * + * @tparam Component Type of component of which to get the sink. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_destroy() { + return assure()->on_destroy(); + } + + /** + * @brief Returns a view for the given components. + * + * This kind of objects are created on the fly and share with the registry + * its internal data structures.
+ * Feel free to discard a view after the use. Creating and destroying a view + * is an incredibly cheap operation because they do not require any type of + * initialization.
+ * As a rule of thumb, storing a view should never be an option. + * + * Views do their best to iterate the smallest set of candidate entities. + * In particular: + * + * * Single component views are incredibly fast and iterate a packed array + * of entities, all of which has the given component. + * * Multi component views look at the number of entities available for each + * component and pick up a reference to the smallest set of candidates to + * test for the given components. + * + * Views in no way affect the functionalities of the registry nor those of + * the underlying pools. + * + * @note + * Multi component views are pretty fast. However their performance tend to + * degenerate when the number of components to iterate grows up and the most + * of the entities have all the given components.
+ * To get a performance boost, consider using a group instead. + * + * @tparam Component Type of components used to construct the view. + * @tparam Exclude Types of components used to filter the view. + * @return A newly created view. + */ + template + [[nodiscard]] basic_view, Component...> view(exclude_t = {}) const { + static_assert(sizeof...(Component) > 0, "Exclusion-only views are not supported"); + static_assert((std::is_const_v && ...), "Invalid non-const type"); + return { *assure>()..., *assure()... }; + } + + /*! @copydoc view */ + template + [[nodiscard]] basic_view, Component...> view(exclude_t = {}) { + static_assert(sizeof...(Component) > 0, "Exclusion-only views are not supported"); + return { *assure>()..., *assure()... }; + } + + /** + * @brief Returns a runtime view for the given components. + * + * This kind of objects are created on the fly and share with the registry + * its internal data structures.
+ * Users should throw away the view after use. Fortunately, creating and + * destroying a runtime view is an incredibly cheap operation because they + * do not require any type of initialization.
+ * As a rule of thumb, storing a view should never be an option. + * + * Runtime views are to be used when users want to construct a view from + * some external inputs and don't know at compile-time what are the required + * components. + * + * @tparam ItComp Type of input iterator for the components to use to + * construct the view. + * @tparam ItExcl Type of input iterator for the components to use to filter + * the view. + * @param first An iterator to the first element of the range of components + * to use to construct the view. + * @param last An iterator past the last element of the range of components + * to use to construct the view. + * @param from An iterator to the first element of the range of components + * to use to filter the view. + * @param to An iterator past the last element of the range of components to + * use to filter the view. + * @return A newly created runtime view. + */ + template + [[nodiscard]] basic_runtime_view runtime_view(ItComp first, ItComp last, ItExcl from = {}, ItExcl to = {}) const { + std::vector *> component(std::distance(first, last)); + std::vector *> filter(std::distance(from, to)); + + std::transform(first, last, component.begin(), [this](const auto ctype) { + const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.poly && pdata.poly->value_type().hash() == ctype; }); + return it == pools.cend() ? nullptr : it->pool.get(); + }); + + std::transform(from, to, filter.begin(), [this](const auto ctype) { + const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.poly && pdata.poly->value_type().hash() == ctype; }); + return it == pools.cend() ? nullptr : it->pool.get(); + }); + + return { std::move(component), std::move(filter) }; + } + + /** + * @brief Returns a group for the given components. + * + * This kind of objects are created on the fly and share with the registry + * its internal data structures.
+ * Feel free to discard a group after the use. Creating and destroying a + * group is an incredibly cheap operation because they do not require any + * type of initialization, but for the first time they are requested.
+ * 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.
+ * However, groups also affect some features of the registry such as the + * creation and destruction of components, which will consequently be + * slightly slower (nothing that can be noticed in most cases). + * + * @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 + [[nodiscard]] basic_group, get_t, Owned...> group(get_t, exclude_t = {}) { + 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, get_t...>, std::remove_const_t...>; + + const auto cpools = std::make_tuple(assure>()..., assure>()...); + constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); + handler_type *handler = nullptr; + + if(auto it = std::find_if(groups.cbegin(), groups.cend(), [size](const auto &gdata) { + return gdata.size == size + && (gdata.owned(type_hash>::value()) && ...) + && (gdata.get(type_hash>::value()) && ...) + && (gdata.exclude(type_hash::value()) && ...); + }); it != groups.cend()) + { + handler = static_cast(it->group.get()); + } + + if(!handler) { + group_data candidate = { + size, + { new handler_type{}, [](void *instance) { delete static_cast(instance); } }, + []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash>::value()) || ...); }, + []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash>::value()) || ...); }, + []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash::value()) || ...); }, + }; + + handler = static_cast(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 { + ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [size](const auto &gdata) { + const auto overlapping = (0u + ... + gdata.owned(type_hash>::value())); + const auto sz = overlapping + (0u + ... + gdata.get(type_hash>::value())) + (0u + ... + gdata.exclude(type_hash::value())); + return !overlapping || ((sz == size) || (sz == gdata.size)); + })); + + const auto next = std::find_if_not(groups.cbegin(), groups.cend(), [size](const auto &gdata) { + return !(0u + ... + gdata.owned(type_hash>::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>::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>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); + (on_construct>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); + (on_destroy().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>(*handler), ...); + + (on_destroy>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); + (on_destroy>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); + (on_construct().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); + + if constexpr(sizeof...(Owned) == 0) { + for(const auto entity: view(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...>>>(*this, *first); + } + } + } + + return { handler->current, *std::get> *>(cpools)..., *std::get> *>(cpools)... }; + } + + /** + * @brief Returns a group for the given components. + * + * @sa group + * + * @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 + [[nodiscard]] basic_group, get_t, Owned...> group_if_exists(get_t, exclude_t = {}) const { + static_assert(std::conjunction_v..., std::is_const...>, "Invalid non-const type"); + + if(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>::value()) && ...) + && (gdata.get(type_hash>::value()) && ...) + && (gdata.exclude(type_hash::value()) && ...); + }); it == groups.cend()) + { + return {}; + } else { + using handler_type = group_handler, get_t...>, std::remove_const_t...>; + return { static_cast(it->group.get())->current, *pool_if_exists>()... , *pool_if_exists>()... }; + } + } + + /** + * @brief Returns a group for the given components. + * + * @sa group + * + * @tparam Owned Types of components owned by the group. + * @tparam Exclude Types of components used to filter the group. + * @return A newly created group. + */ + template + [[nodiscard]] basic_group, get_t<>, Owned...> group(exclude_t = {}) { + return group(get_t<>{}, exclude); + } + + /** + * @brief Returns a group for the given components. + * + * @sa group_if_exists + * + * @tparam Owned Types of components owned by the group. + * @tparam Exclude Types of components used to filter the group. + * @return A newly created group. + */ + template + [[nodiscard]] basic_group, get_t<>, Owned...> group_if_exists(exclude_t = {}) const { + return group_if_exists(get_t<>{}, 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 sortable, false + * otherwise. + */ + template + [[nodiscard]] bool sortable() const { + return std::none_of(groups.cbegin(), groups.cend(), [](auto &&gdata) { return (gdata.owned(type_hash>::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 + [[nodiscard]] bool sortable(const basic_group, get_t, Owned...> &) ENTT_NOEXCEPT { + constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); + return std::find_if(groups.cbegin(), groups.cend(), [size](const auto &gdata) { + return (0u + ... + gdata.owned(type_hash>::value())) && (size < gdata.size); + }) == groups.cend(); + } + + /** + * @brief Sorts the pool of entities for the given component. + * + * The order of the elements in a pool is highly affected by assignments + * of components to entities and deletions. Components are arranged to + * maximize the performance during iterations and users should not make any + * assumption on the order.
+ * This function can be used to impose an order to the elements in the pool + * of the given component. The order is kept valid until a component of the + * given type is assigned or removed from an entity. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * bool(const Component &, const Component &); + * @endcode + * + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function oject must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * The comparison funtion object received by the sort 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 + void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { + ENTT_ASSERT(sortable()); + assure()->sort(std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sorts two pools of components in the same way. + * + * The order of the elements in a pool is highly affected by assignments + * of components to entities and deletions. Components are arranged to + * maximize the performance during iterations and users should not make any + * assumption on the order. + * + * It happens that different pools of components must be sorted the same way + * because of runtime and/or performance constraints. This function can be + * used to order a pool of components according to the order between the + * entities in another pool of components. + * + * @b How @b it @b works + * + * Being `A` and `B` the two sets where `B` is the master (the one the order + * of which rules) and `A` is the slave (the one to sort), after a call to + * this function an iterator for `A` will return the entities according to + * the following rules: + * + * * All the entities in `A` that are also in `B` are returned first + * according to the order they have in `B`. + * * All the entities in `A` that are not in `B` are returned in no + * particular order after all the other entities. + * + * Any subsequent change to `B` won't affect the order in `A`. + * + * @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 + void sort() { + ENTT_ASSERT(sortable()); + assure()->respect(*assure()); + } + + /** + * @brief Visits an entity and returns the type info for its components. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const type_info); + * @endcode + * + * Returned identifiers are those of the components owned by the entity. + * + * @sa type_info + * + * @warning + * It's not specified whether a component attached to or removed from the + * given entity during the visit is returned or not to the caller. + * + * @tparam Func Type of the function object to invoke. + * @param entity A valid entity identifier. + * @param func A valid function object. + */ + template + void visit(entity_type entity, Func func) const { + for(auto pos = pools.size(); pos; --pos) { + if(const auto &pdata = pools[pos-1]; pdata.pool && pdata.pool->contains(entity)) { + func(pdata.poly->value_type()); + } + } + } + + /** + * @brief Visits a registry and returns the type info for its components. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const type_info); + * @endcode + * + * Returned identifiers are those of the components managed by the registry. + * + * @sa type_info + * + * @warning + * It's not specified whether a component for which a pool is created during + * the visit is returned or not to the caller. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void visit(Func func) const { + for(auto pos = pools.size(); pos; --pos) { + if(const auto &pdata = pools[pos-1]; pdata.pool) { + func(pdata.poly->value_type()); + } + } + } + + /** + * @brief Binds an object to the context of the registry. + * + * If the value already exists it is overwritten, otherwise a new instance + * of the given type is created and initialized with the arguments provided. + * + * @tparam Type Type of object to set. + * @tparam Args Types of arguments to use to construct the object. + * @param args Parameters to use to initialize the value. + * @return A reference to the newly created object. + */ + template + Type & set(Args &&... args) { + unset(); + vars.emplace_back(std::in_place_type, std::forward(args)...); + return any_cast(vars.back()); + } + + /** + * @brief Unsets a context variable if it exists. + * @tparam Type Type of object to set. + */ + template + void unset() { + vars.erase(std::remove_if(vars.begin(), vars.end(), [type = type_id()](auto &&var) { return var.type() == type; }), vars.end()); + } + + /** + * @brief Binds an object to the context of the registry. + * + * In case the context doesn't contain the given object, the parameters + * provided are used to construct it. + * + * @tparam Type Type of object to set. + * @tparam Args Types of arguments to use to construct the object. + * @param args Parameters to use to initialize the object. + * @return A reference to the object in the context of the registry. + */ + template + [[nodiscard]] Type & ctx_or_set(Args &&... args) { + auto *value = try_ctx(); + return value ? *value : set(std::forward(args)...); + } + + /** + * @brief Returns a pointer to an object in the context of the registry. + * @tparam Type Type of object to get. + * @return A pointer to the object if it exists in the context of the + * registry, a null pointer otherwise. + */ + template + [[nodiscard]] Type * try_ctx() const { + auto it = std::find_if(vars.cbegin(), vars.cend(), [type = type_id()](auto &&var) { return var.type() == type; }); + return it == vars.cend() ? nullptr : any_cast(&*it); + } + + /*! @copydoc try_ctx */ + template + [[nodiscard]] Type * try_ctx() { + auto it = std::find_if(vars.begin(), vars.end(), [type = type_id()](auto &&var) { return var.type() == type; }); + return it == vars.end() ? nullptr : any_cast(&*it); + } + + /** + * @brief Returns a reference to an object in the context of the registry. + * + * @warning + * Attempting to get a context variable that doesn't exist results in + * undefined behavior. + * + * @tparam Type Type of object to get. + * @return A valid reference to the object in the context of the registry. + */ + template + [[nodiscard]] Type & ctx() const { + auto it = std::find_if(vars.cbegin(), vars.cend(), [type = type_id()](auto &&var) { return var.type() == type; }); + ENTT_ASSERT(it != vars.cend()); + return any_cast(*it); + } + + /*! @copydoc ctx */ + template + [[nodiscard]] Type & ctx() { + auto it = std::find_if(vars.begin(), vars.end(), [type = type_id()](auto &&var) { return var.type() == type; }); + ENTT_ASSERT(it != vars.end()); + return any_cast(*it); + } + + /** + * @brief Visits a registry and returns the type info for its context + * variables. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const type_info); + * @endcode + * + * Returned identifiers are those of the context variables currently set. + * + * @sa type_info + * + * @warning + * It's not specified whether a context variable created during the visit is + * returned or not to the caller. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void ctx(Func func) const { + for(auto pos = vars.size(); pos; --pos) { + func(vars[pos-1].type()); + } + } + +private: + std::vector> vars{}; + mutable std::vector pools{}; + std::vector groups{}; + std::vector entities{}; + entity_type available{null}; +}; + + +} + + +#endif // #include "entity/runtime_view.hpp" +#ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP +#define ENTT_ENTITY_RUNTIME_VIEW_HPP + + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "sparse_set.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Runtime view. + * + * Runtime views iterate over those entities that have at least all the given + * components in their bags. During initialization, a runtime view looks at the + * number of entities available for each component and picks up a reference to + * the smallest set of candidate entities in order to get a performance boost + * when iterate.
+ * Order of elements during iterations are highly dependent on the order of the + * underlying data structures. See sparse_set and its specializations for more + * details. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all the other cases, modifying the pools of the given components in any + * way invalidates all the iterators and using them results in undefined + * behavior. + * + * @note + * Views share references to the underlying data structures of the registry that + * generated them. Therefore any change to the entities and to the components + * made by means of the registry are immediately reflected by the views, unless + * a pool was missing when the view was built (in this case, the view won't + * have a valid reference and won't be updated accordingly). + * + * @warning + * Lifetime of a view must not overcome that of the registry that generated it. + * In any other case, attempting to use a view results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_runtime_view final { + using underlying_iterator = typename basic_sparse_set::iterator; + + class view_iterator final { + friend class basic_runtime_view; + + view_iterator(const std::vector *> &cpools, const std::vector *> &ignore, underlying_iterator curr) ENTT_NOEXCEPT + : pools{&cpools}, + filter{&ignore}, + it{curr} + { + if(it != (*pools)[0]->end() && !valid()) { + ++(*this); + } + } + + [[nodiscard]] bool valid() const { + return std::all_of(pools->begin()++, pools->end(), [entt = *it](const auto *curr) { return curr->contains(entt); }) + && std::none_of(filter->cbegin(), filter->cend(), [entt = *it](const auto *curr) { return curr && curr->contains(entt); }); + } + + public: + using difference_type = typename underlying_iterator::difference_type; + using value_type = typename underlying_iterator::value_type; + using pointer = typename underlying_iterator::pointer; + using reference = typename underlying_iterator::reference; + using iterator_category = std::bidirectional_iterator_tag; + + view_iterator() ENTT_NOEXCEPT = default; + + view_iterator & operator++() { + while(++it != (*pools)[0]->end() && !valid()); + return *this; + } + + view_iterator operator++(int) { + view_iterator orig = *this; + return ++(*this), orig; + } + + view_iterator & operator--() ENTT_NOEXCEPT { + while(--it != (*pools)[0]->begin() && !valid()); + return *this; + } + + view_iterator operator--(int) ENTT_NOEXCEPT { + view_iterator orig = *this; + return operator--(), orig; + } + + [[nodiscard]] bool operator==(const view_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + [[nodiscard]] bool operator!=(const view_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + [[nodiscard]] pointer operator->() const { + return it.operator->(); + } + + [[nodiscard]] reference operator*() const { + return *operator->(); + } + + private: + const std::vector *> *pools; + const std::vector *> *filter; + underlying_iterator it; + }; + + [[nodiscard]] bool valid() const { + return !pools.empty() && pools.front(); + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Bidirectional iterator type. */ + using iterator = view_iterator; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_runtime_view() ENTT_NOEXCEPT + : pools{}, + filter{} + {} + + /** + * @brief Constructs a runtime view from a set of storage classes. + * @param cpools The storage for the types to iterate. + * @param epools The storage for the types used to filter the view. + */ + basic_runtime_view(std::vector *> cpools, std::vector *> epools) ENTT_NOEXCEPT + : pools{std::move(cpools)}, + filter{std::move(epools)} + { + const auto it = std::min_element(pools.begin(), pools.end(), [](const auto *lhs, const auto *rhs) { + return (!lhs && rhs) || (lhs && rhs && lhs->size() < rhs->size()); + }); + + // brings the best candidate (if any) on front of the vector + std::rotate(pools.begin(), it, pools.end()); + } + + /** + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. + */ + [[nodiscard]] size_type size_hint() const { + return valid() ? pools.front()->size() : size_type{}; + } + + /** + * @brief Returns an iterator to the first entity that has the given + * components. + * + * The returned iterator points to the first entity that has the given + * components. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @return An iterator to the first entity that has the given components. + */ + [[nodiscard]] iterator begin() const { + return valid() ? iterator{pools, filter, pools[0]->begin()} : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given components. + * + * The returned iterator points to the entity following the last entity that + * has the given components. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the entity following the last entity that has the + * given components. + */ + [[nodiscard]] iterator end() const { + return valid() ? iterator{pools, filter, pools[0]->end()} : iterator{}; + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid entity identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const { + return valid() && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); }) + && std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); }); + } + + /** + * @brief Iterates entities and applies the given function object to them. + * + * The function object is invoked for each entity. It is provided only with + * the entity itself. To get the components, users can use the registry with + * which the view was built.
+ * 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 + void each(Func func) const { + for(const auto entity: *this) { + func(entity); + } + } + +private: + std::vector *> pools; + std::vector *> filter; +}; + + +} + + +#endif // #include "entity/snapshot.hpp" #ifndef ENTT_ENTITY_SNAPSHOT_HPP @@ -10376,18 +22272,23 @@ private: #include -#include #include -#include #include +#include #include #include +#include +#include // #include "../config/config.h" +// #include "../core/type_traits.hpp" + // #include "entity.hpp" // #include "fwd.hpp" +// #include "registry.hpp" + namespace entt { @@ -10405,39 +22306,33 @@ namespace entt { */ template class basic_snapshot { - /*! @brief A registry is allowed to create snapshots. */ - friend class basic_registry; - using traits_type = entt_traits; template void get(Archive &archive, std::size_t sz, It first, It last) const { + const auto view = reg->template view>(); archive(typename traits_type::entity_type(sz)); while(first != last) { const auto entt = *(first++); - if(reg->template has(entt)) { - if constexpr(std::is_empty_v) { - archive(entt); - } else { - archive(entt, reg->template get(entt)); - } + if(reg->template all_of(entt)) { + std::apply(archive, std::tuple_cat(std::make_tuple(entt), view.get(entt))); } } } - template - void component(Archive &archive, It first, It last, std::index_sequence) const { - std::array size{}; + template + void component(Archive &archive, It first, It last, std::index_sequence) const { + std::array size{}; auto begin = first; while(begin != last) { const auto entt = *(begin++); - ((reg->template has(entt) ? ++size[Indexes] : size[Indexes]), ...); + ((reg->template all_of(entt) ? ++size[Index] : size[Index]), ...); } - (get(archive, size[Indexes], first, last), ...); + (get(archive, size[Index], first, last), ...); } public: @@ -10471,15 +22366,15 @@ public: template const basic_snapshot & entities(Archive &archive) const { const auto sz = reg->size(); - auto first = reg->data(); - const auto last = first + sz; archive(typename traits_type::entity_type(sz)); - while(first != last) { - archive(*(first++)); + for(auto first = reg->data(), last = first + sz; first != last; ++first) { + archive(*first); } + archive(reg->destroyed()); + return *this; } @@ -10496,8 +22391,14 @@ public: */ template const basic_snapshot & component(Archive &archive) const { - (component(archive, reg->template data(), reg->template data() + reg->template size()), ...); - return *this; + if constexpr(sizeof...(Component) == 1u) { + const auto view = reg->template view(); + (component(archive, view.data(), view.data() + view.size()), ...); + return *this; + } else { + (component(archive), ...); + return *this; + } } /** @@ -10537,9 +22438,6 @@ private: */ template class basic_snapshot_loader { - /*! @brief A registry is allowed to create snapshot loaders. */ - friend class basic_registry; - using traits_type = entt_traits; template @@ -10549,7 +22447,7 @@ class basic_snapshot_loader { entity_type entt{}; - if constexpr(std::is_empty_v) { + if constexpr(std::tuple_size_vtemplate view().get({}))> == 0) { while(length--) { archive(entt); const auto entity = reg->valid(entt) ? entt : reg->create(entt); @@ -10610,7 +22508,10 @@ public: archive(all[pos]); } - reg->assign(all.cbegin(), all.cend()); + entity_type destroyed; + archive(destroyed); + + reg->assign(all.cbegin(), all.cend(), destroyed); return *this; } @@ -10678,9 +22579,7 @@ class basic_continuous_loader { using traits_type = entt_traits; void destroy(Entity entt) { - const auto it = remloc.find(entt); - - if(it == remloc.cend()) { + 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); @@ -10694,7 +22593,10 @@ class basic_continuous_loader { const auto local = reg->create(); remloc.emplace(entt, std::make_pair(local, true)); } else { - remloc[entt].first = reg->valid(remloc[entt].first) ? remloc[entt].first : reg->create(); + if(!reg->valid(remloc[entt].first)) { + remloc[entt].first = reg->create(); + } + // set the dirty flag remloc[entt].second = true; } @@ -10764,7 +22666,7 @@ class basic_continuous_loader { entity_type entt{}; - if constexpr(std::is_empty_v) { + if constexpr(std::tuple_size_vtemplate view().get({}))> == 0) { while(length--) { archive(entt); restore(entt); @@ -10827,6 +22729,9 @@ public: } } + // discards the head of the list of destroyed entities + archive(entt); + return *this; } @@ -10941,12 +22846,2502 @@ private: #endif // #include "entity/sparse_set.hpp" +#ifndef ENTT_ENTITY_SPARSE_SET_HPP +#define ENTT_ENTITY_SPARSE_SET_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/algorithm.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Basic sparse set implementation. + * + * Sparse set or packed array or whatever is the name users give it.
+ * 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.
+ * 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). + */ +template +class basic_sparse_set { + static constexpr auto page_size = ENTT_PAGE_SIZE; + + using traits_type = entt_traits; + using page_type = std::unique_ptr; + + class sparse_set_iterator final { + friend class basic_sparse_set; + + using packed_type = std::vector; + using index_type = typename traits_type::difference_type; + + sparse_set_iterator(const packed_type &ref, const index_type idx) ENTT_NOEXCEPT + : packed{&ref}, index{idx} + {} + + public: + using difference_type = index_type; + using value_type = Entity; + using pointer = const value_type *; + using reference = const value_type &; + using iterator_category = std::random_access_iterator_tag; + + sparse_set_iterator() ENTT_NOEXCEPT = default; + + sparse_set_iterator & operator++() ENTT_NOEXCEPT { + return --index, *this; + } + + sparse_set_iterator operator++(int) ENTT_NOEXCEPT { + iterator orig = *this; + return ++(*this), orig; + } + + sparse_set_iterator & operator--() ENTT_NOEXCEPT { + return ++index, *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 { + index -= 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); + } + + difference_type operator-(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return other.index - index; + } + + [[nodiscard]] reference operator[](const difference_type value) const { + const auto pos = size_type(index-value-1u); + return (*packed)[pos]; + } + + [[nodiscard]] bool operator==(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return other.index == index; + } + + [[nodiscard]] bool operator!=(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + [[nodiscard]] bool operator<(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return index > other.index; + } + + [[nodiscard]] bool operator>(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return index < other.index; + } + + [[nodiscard]] bool operator<=(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return !(*this > other); + } + + [[nodiscard]] bool operator>=(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return !(*this < other); + } + + [[nodiscard]] pointer operator->() const { + const auto pos = size_type(index-1u); + return &(*packed)[pos]; + } + + [[nodiscard]] reference operator*() const { + return *operator->(); + } + + private: + const packed_type *packed; + index_type index; + }; + + [[nodiscard]] auto page(const Entity entt) const ENTT_NOEXCEPT { + return size_type{(to_integral(entt) & traits_type::entity_mask) / page_size}; + } + + [[nodiscard]] auto offset(const Entity entt) const ENTT_NOEXCEPT { + return size_type{to_integral(entt) & (page_size - 1)}; + } + + [[nodiscard]] page_type & assure(const std::size_t pos) { + if(!(pos < sparse.size())) { + sparse.resize(pos+1); + } + + if(!sparse[pos]) { + sparse[pos].reset(new entity_type[page_size]); + // null is safe in all cases for our purposes + for(auto *first = sparse[pos].get(), *last = first + page_size; first != last; ++first) { + *first = null; + } + } + + return sparse[pos]; + } + +protected: + /*! @brief Swaps two entities in the internal packed array. */ + virtual void swap_at(const std::size_t, const std::size_t) {} + + /*! @brief Attempts to remove an entity from the internal packed array. */ + virtual void swap_and_pop(const std::size_t, void *) {} + +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 = sparse_set_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = const entity_type *; + + /*! @brief Default constructor. */ + basic_sparse_set() = default; + + /*! @brief Default move constructor. */ + basic_sparse_set(basic_sparse_set &&) = default; + + /*! @brief Default destructor. */ + virtual ~basic_sparse_set() = default; + + /*! @brief Default move assignment operator. @return This sparse set. */ + basic_sparse_set & operator=(basic_sparse_set &&) = default; + + /** + * @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. + */ + 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]] size_type capacity() const ENTT_NOEXCEPT { + return packed.capacity(); + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + // conservative approach + if(packed.empty()) { + sparse.clear(); + } + + sparse.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() * 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. + * + * 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 internal packed array. + */ + [[nodiscard]] const entity_type * 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 internal packed array. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + const typename traits_type::difference_type pos = packed.size(); + return iterator{packed, pos}; + } + + /** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last entity in + * the internal packed array. Attempting to dereference the returned + * iterator results in undefined behavior. + * + * @return An iterator to the element following the last entity of the + * internal packed array. + */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return iterator{packed, {}}; + } + + /** + * @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]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return packed.data(); + } + + /** + * @brief Returns a reverse iterator to the end. + * + * The returned iterator points to the element following the last entity in + * the reversed internal packed array. Attempting to dereference the + * returned iterator results in undefined behavior. + * + * @return An iterator to the element following the last entity of the + * reversed internal packed array. + */ + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return rbegin() + packed.size(); + } + + /** + * @brief Finds an entity. + * @param entt A valid entity identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const { + return contains(entt) ? --(end() - index(entt)) : end(); + } + + /** + * @brief Checks if a sparse set contains an entity. + * @param entt A valid entity identifier. + * @return True if the sparse set contains the entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const { + const auto curr = page(entt); + // testing against null permits to avoid accessing the packed array + return (curr < sparse.size() && sparse[curr] && sparse[curr][offset(entt)] != null); + } + + /** + * @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 entity identifier. + * @return The position of the entity in the sparse set. + */ + [[nodiscard]] size_type index(const entity_type entt) const { + ENTT_ASSERT(contains(entt)); + return size_type{to_integral(sparse[page(entt)][offset(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 { + 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_ASSERT(pos < packed.size()); + return packed[pos]; + } + + /** + * @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 entity identifier. + */ + void emplace(const entity_type entt) { + ENTT_ASSERT(!contains(entt)); + assure(page(entt))[offset(entt)] = entity_type{static_cast(packed.size())}; + packed.push_back(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. + */ + template + void insert(It first, It last) { + auto next = static_cast(packed.size()); + packed.insert(packed.end(), first, last); + + for(; first != last; ++first) { + ENTT_ASSERT(!contains(*first)); + assure(page(*first))[offset(*first)] = entity_type{next++}; + } + } + + /** + * @brief Removes an entity from a sparse set. + * + * @warning + * Attempting to remove an entity that doesn't belong to the sparse set + * results in undefined behavior. + * + * @param entt A valid entity identifier. + * @param ud Optional user data that are forwarded as-is to derived classes. + */ + void remove(const entity_type entt, void *ud = nullptr) { + ENTT_ASSERT(contains(entt)); + auto &ref = sparse[page(entt)][offset(entt)]; + + // last chance to use the entity for derived classes and mixins, if any + swap_and_pop(size_type{to_integral(ref)}, ud); + + const auto other = packed.back(); + sparse[page(other)][offset(other)] = ref; + // if it looks weird, imagine what the subtle bugs it prevents are + ENTT_ASSERT((packed.back() = entt, true)); + packed[size_type{to_integral(ref)}] = other; + ref = null; + + packed.pop_back(); + } + + /** + * @brief Removes multiple entities from a pool. + * @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 ud Optional user data that are forwarded as-is to derived classes. + */ + template + void remove(It first, It last, void *ud = nullptr) { + for(; first != last; ++first) { + remove(*first, ud); + } + } + + /** + * @copybrief swap_at + * + * 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 entity identifier. + * @param rhs A valid entity identifier. + */ + void swap(const entity_type lhs, const entity_type rhs) { + const auto from = index(lhs); + const auto to = index(rhs); + std::swap(sparse[page(lhs)][offset(lhs)], sparse[page(rhs)][offset(rhs)]); + std::swap(packed[from], packed[to]); + swap_at(from, 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 count Number of elements to sort. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort_n(const size_type count, Compare compare, Sort algo = Sort{}, Args &&... args) { + ENTT_ASSERT(!(count > size())); + + algo(packed.rend() - count, packed.rend(), std::move(compare), std::forward(args)...); + + for(size_type pos{}; pos < count; ++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); + sparse[page(entt)][offset(entt)] = entity_type{static_cast(curr)}; + + curr = next; + 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 + void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { + sort_n(size(), std::move(compare), std::move(algo), std::forward(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.
+ * 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) { + const auto to = other.end(); + auto from = other.begin(); + + size_type pos = packed.size() - 1; + + while(pos && from != to) { + if(contains(*from)) { + if(*from != packed[pos]) { + swap(packed[pos], *from); + } + + --pos; + } + + ++from; + } + } + + /** + * @brief Clears a sparse set. + * @param ud Optional user data that are forwarded as-is to derived classes. + */ + void clear(void *ud = nullptr) ENTT_NOEXCEPT { + remove(begin(), end(), ud); + } + +private: + std::vector sparse; + std::vector packed; +}; + + +} + + +#endif // #include "entity/storage.hpp" +#ifndef ENTT_ENTITY_STORAGE_HPP +#define ENTT_ENTITY_STORAGE_HPP + + +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/algorithm.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../signal/sigh.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" + + + +namespace entt { + + +/** + * @brief Basic storage implementation. + * + * This class is a refinement of a sparse set that associates an object to an + * entity. The main purpose of this class is to extend sparse sets to store + * components in a registry. It guarantees fast access both to the elements and + * to the entities. + * + * @note + * Entities and objects have the same order. It's guaranteed both in case of raw + * access (either to entities or objects) and when using random or input access + * iterators. + * + * @note + * Internal data structures arrange elements to maximize performance. There are + * no guarantees that objects are returned in the insertion order when iterate + * a storage. Do not make assumption on the order in any case. + * + * @warning + * Empty types aren't explicitly instantiated. Therefore, many of the functions + * normally available for non-empty types will not be available for empty ones. + * + * @sa sparse_set + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Type of objects assigned to the entities. + */ +template +class basic_storage: public basic_sparse_set { + static_assert(std::is_move_constructible_v && std::is_move_assignable_v, "The managed type must be at least move constructible and assignable"); + + using underlying_type = basic_sparse_set; + using traits_type = entt_traits; + + template + class storage_iterator final { + friend class basic_storage; + + using instance_type = constness_as_t, Value>; + using index_type = typename traits_type::difference_type; + + storage_iterator(instance_type &ref, const index_type idx) ENTT_NOEXCEPT + : instances{&ref}, index{idx} + {} + + public: + using difference_type = index_type; + using value_type = Value; + using pointer = value_type *; + using reference = value_type &; + using iterator_category = std::random_access_iterator_tag; + + storage_iterator() ENTT_NOEXCEPT = default; + + storage_iterator & operator++() ENTT_NOEXCEPT { + return --index, *this; + } + + storage_iterator operator++(int) ENTT_NOEXCEPT { + storage_iterator orig = *this; + return ++(*this), orig; + } + + storage_iterator & operator--() ENTT_NOEXCEPT { + return ++index, *this; + } + + storage_iterator operator--(int) ENTT_NOEXCEPT { + storage_iterator orig = *this; + return operator--(), orig; + } + + storage_iterator & operator+=(const difference_type value) ENTT_NOEXCEPT { + index -= value; + return *this; + } + + storage_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + storage_iterator copy = *this; + return (copy += value); + } + + storage_iterator & operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + storage_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + difference_type operator-(const storage_iterator &other) const ENTT_NOEXCEPT { + return other.index - index; + } + + [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + const auto pos = size_type(index-value-1); + return (*instances)[pos]; + } + + [[nodiscard]] bool operator==(const storage_iterator &other) const ENTT_NOEXCEPT { + return other.index == index; + } + + [[nodiscard]] bool operator!=(const storage_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + [[nodiscard]] bool operator<(const storage_iterator &other) const ENTT_NOEXCEPT { + return index > other.index; + } + + [[nodiscard]] bool operator>(const storage_iterator &other) const ENTT_NOEXCEPT { + return index < other.index; + } + + [[nodiscard]] bool operator<=(const storage_iterator &other) const ENTT_NOEXCEPT { + return !(*this > other); + } + + [[nodiscard]] bool operator>=(const storage_iterator &other) const ENTT_NOEXCEPT { + return !(*this < other); + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + const auto pos = size_type(index-1u); + return &(*instances)[pos]; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + private: + instance_type *instances; + index_type index; + }; + +protected: + /** + * @copybrief basic_sparse_set::swap_at + * @param lhs A valid position of an entity within storage. + * @param rhs A valid position of an entity within storage. + */ + void swap_at(const std::size_t lhs, const std::size_t rhs) { + std::swap(instances[lhs], instances[rhs]); + } + + /** + * @copybrief basic_sparse_set::swap_and_pop + * @param pos A valid position of an entity within storage. + */ + void swap_and_pop(const std::size_t pos, void *) { + auto other = std::move(instances.back()); + instances[pos] = std::move(other); + instances.pop_back(); + } + +public: + /*! @brief Type of the objects assigned to entities. */ + using value_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = storage_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = storage_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = Type *; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = const Type *; + + /** + * @brief Increases the capacity of a storage. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + void reserve(const size_type cap) { + underlying_type::reserve(cap); + instances.reserve(cap); + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + underlying_type::shrink_to_fit(); + instances.shrink_to_fit(); + } + + /** + * @brief Direct access to the array of objects. + * + * The returned pointer is such that range `[raw(), raw() + size())` is + * always a valid range, even if the container is empty. + * + * @note + * Objects are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @return A pointer to the array of objects. + */ + [[nodiscard]] const value_type * raw() const ENTT_NOEXCEPT { + return instances.data(); + } + + /*! @copydoc raw */ + [[nodiscard]] value_type * raw() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).raw()); + } + + /** + * @brief Returns an iterator to the beginning. + * + * The returned iterator points to the first instance of the internal array. + * If the storage is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { + const typename traits_type::difference_type pos = underlying_type::size(); + return const_iterator{instances, pos}; + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() ENTT_NOEXCEPT { + const typename traits_type::difference_type pos = underlying_type::size(); + return iterator{instances, pos}; + } + + /** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the internal array. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { + return const_iterator{instances, {}}; + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() ENTT_NOEXCEPT { + return iterator{instances, {}}; + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * The returned iterator points to the first instance of the reversed + * internal array. If the storage is empty, the returned iterator will be + * equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT { + return instances.data(); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() ENTT_NOEXCEPT { + return instances.data(); + } + + /** + * @brief Returns a reverse iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the reversed internal array. Attempting to dereference the returned + * iterator results in undefined behavior. + * + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT { + return crbegin() + instances.size(); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const ENTT_NOEXCEPT { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() ENTT_NOEXCEPT { + return rbegin() + instances.size(); + } + + /** + * @brief Returns the object assigned to an entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid entity identifier. + * @return The object assigned to the entity. + */ + [[nodiscard]] const value_type & get(const entity_type entt) const { + return instances[underlying_type::index(entt)]; + } + + /*! @copydoc get */ + [[nodiscard]] value_type & get(const entity_type entt) { + return const_cast(std::as_const(*this).get(entt)); + } + + /** + * @brief Assigns an entity to a storage and constructs its object. + * + * This version accept both types that can be constructed in place directly + * and types like aggregates that do not work well with a placement new as + * performed usually under the hood during an _emplace back_. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid entity identifier. + * @param args Parameters to use to construct an object for the entity. + * @return A reference to the newly created object. + */ + template + value_type & emplace(const entity_type entt, Args &&... args) { + if constexpr(std::is_aggregate_v) { + instances.push_back(Type{std::forward(args)...}); + } else { + instances.emplace_back(std::forward(args)...); + } + + // entity goes after component in case constructor throws + underlying_type::emplace(entt); + return instances.back(); + } + + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entity A valid entity identifier. + * @param func Valid function objects. + * @return A reference to the updated instance. + */ + template + decltype(auto) patch(const entity_type entity, Func &&... func) { + auto &&instance = instances[this->index(entity)]; + (std::forward(func)(instance), ...); + return instance; + } + + /** + * @brief Assigns one or more entities to a storage and constructs their + * objects from a given instance. + * + * @warning + * Attempting to assign an entity that already belongs to the storage + * results in undefined behavior. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param value An instance of the object to construct. + */ + template + void insert(It first, It last, const value_type &value = {}) { + instances.insert(instances.end(), std::distance(first, last), value); + // entities go after components in case constructors throw + underlying_type::insert(first, last); + } + + /** + * @brief Assigns one or more entities to a storage and constructs their + * objects from a given range. + * + * @sa construct + * + * @tparam EIt Type of input iterator. + * @tparam CIt Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param from An iterator to the first element of the range of objects. + * @param to An iterator past the last element of the range of objects. + */ + template + void insert(EIt first, EIt last, CIt from, CIt to) { + instances.insert(instances.end(), from, to); + // entities go after components in case constructors throw + underlying_type::insert(first, last); + } + + /** + * @brief Sort elements according to the given comparison function. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * bool(const Type &, const Type &); + * @endcode + * + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function oject must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @warning + * Empty types are never instantiated. Therefore, only comparison function + * objects that require to return entities rather than components are + * accepted. + * + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param count Number of elements to sort. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort_n(const size_type count, Compare compare, Sort algo = Sort{}, Args &&... args) { + if constexpr(std::is_invocable_v) { + underlying_type::sort_n(count, [this, compare = std::move(compare)](const auto lhs, const auto rhs) { + return compare(std::as_const(instances[underlying_type::index(lhs)]), std::as_const(instances[underlying_type::index(rhs)])); + }, std::move(algo), std::forward(args)...); + } else { + underlying_type::sort_n(count, std::move(compare), std::move(algo), std::forward(args)...); + } + } + + /** + * @brief Sort all elements according to the given comparison function. + * + * @sa sort_n + * + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { + sort_n(this->size(), std::move(compare), std::move(algo), std::forward(args)...); + } + +private: + std::vector instances; +}; + + +/*! @copydoc basic_storage */ +template +class basic_storage>>: public basic_sparse_set { + using underlying_type = basic_sparse_set; + +public: + /*! @brief Type of the objects assigned to entities. */ + using value_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + + /** + * @brief Fake get function. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid entity identifier. + */ + void get([[maybe_unused]] const entity_type entt) const { + ENTT_ASSERT(this->contains(entt)); + } + + /** + * @brief Assigns an entity to a storage and constructs its object. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid entity identifier. + * @param args Parameters to use to construct an object for the entity. + */ + template + void emplace(const entity_type entt, Args &&... args) { + [[maybe_unused]] value_type instance{std::forward(args)...}; + underlying_type::emplace(entt); + } + + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entity A valid entity identifier. + * @param func Valid function objects. + */ + template + void patch([[maybe_unused]] const entity_type entity, Func &&... func) { + ENTT_ASSERT(this->contains(entity)); + (std::forward(func)(), ...); + } + + /** + * @brief Assigns one or more entities to a storage. + * + * @warning + * Attempting to assign an entity that already belongs to the storage + * results in undefined behavior. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void insert(It first, It last, const value_type & = {}) { + underlying_type::insert(first, last); + } +}; + + +/** + * @brief Mixin type to use to wrap basic storage classes. + * @tparam Type The type of the underlying storage. + */ +template +struct storage_adapter_mixin: Type { + static_assert(std::is_same_v>, "Invalid object type"); + + /*! @brief Type of the objects assigned to entities. */ + using value_type = typename Type::value_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + + /** + * @brief Assigns entities to a storage. + * @tparam Args Types of arguments to use to construct the object. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the object. + * @return A reference to the newly created object. + */ + template + decltype(auto) emplace(basic_registry &, const entity_type entity, Args &&... args) { + return Type::emplace(entity, std::forward(args)...); + } + + /** + * @brief Assigns entities to a storage. + * @tparam It Type of input iterator. + * @tparam Args Types of arguments to use to construct the objects assigned + * to the entities. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param args Parameters to use to initialize the objects assigned to the + * entities. + */ + template + void insert(basic_registry &, It first, It last, Args &&... args) { + Type::insert(first, last, std::forward(args)...); + } + + /** + * @brief Patches the given instance for an entity. + * @tparam Func Types of the function objects to invoke. + * @param entity A valid entity identifier. + * @param func Valid function objects. + * @return A reference to the patched instance. + */ + template + decltype(auto) patch(basic_registry &, const entity_type entity, Func &&... func) { + return Type::patch(entity, std::forward(func)...); + } +}; + + +/** + * @brief Mixin type to use to add signal support to storage types. + * @tparam Type The type of the underlying storage. + */ +template +class sigh_storage_mixin final: public Type { + /** + * @copybrief basic_sparse_set::swap_and_pop + * @param pos A valid position of an entity within storage. + * @param ud Optional user data that are forwarded as-is to derived classes. + */ + void swap_and_pop(const std::size_t pos, void *ud) final { + ENTT_ASSERT(ud != nullptr); + const auto entity = basic_sparse_set::operator[](pos); + destruction.publish(*static_cast *>(ud), entity); + // the position may have changed due to the actions of a listener + Type::swap_and_pop(this->index(entity), ud); + } + +public: + /*! @brief Underlying value type. */ + using value_type = typename Type::value_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever a new instance is created and assigned to an entity.
+ * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, entity_type); + * @endcode + * + * Listeners are invoked **after** the object has been assigned to the + * entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_construct() ENTT_NOEXCEPT { + return sink{construction}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is explicitly updated.
+ * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, entity_type); + * @endcode + * + * Listeners are invoked **after** the object has been updated. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_update() ENTT_NOEXCEPT { + return sink{update}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is removed from an entity and thus destroyed.
+ * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, entity_type); + * @endcode + * + * Listeners are invoked **before** the object has been removed from the + * entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_destroy() ENTT_NOEXCEPT { + return sink{destruction}; + } + + /** + * @brief Assigns entities to a storage. + * @tparam Args Types of arguments to use to construct the object. + * @param owner The registry that issued the request. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the object. + * @return A reference to the newly created object. + */ + template + decltype(auto) emplace(basic_registry &owner, const entity_type entity, Args &&... args) { + Type::emplace(entity, std::forward(args)...); + construction.publish(owner, entity); + return this->get(entity); + } + + /** + * @brief Assigns entities to a storage. + * @tparam It Type of input iterator. + * @tparam Args Types of arguments to use to construct the objects assigned + * to the entities. + * @param owner The registry that issued the request. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param args Parameters to use to initialize the objects assigned to the + * entities. + */ + template + void insert(basic_registry &owner, It first, It last, Args &&... args) { + Type::insert(first, last, std::forward(args)...); + + if(!construction.empty()) { + for(; first != last; ++first) { + construction.publish(owner, *first); + } + } + } + + /** + * @brief Patches the given instance for an entity. + * @tparam Func Types of the function objects to invoke. + * @param owner The registry that issued the request. + * @param entity A valid entity identifier. + * @param func Valid function objects. + * @return A reference to the patched instance. + */ + template + decltype(auto) patch(basic_registry &owner, const entity_type entity, Func &&... func) { + Type::patch(entity, std::forward(func)...); + update.publish(owner, entity); + return this->get(entity); + } + +private: + sigh &, const entity_type)> construction{}; + sigh &, const entity_type)> destruction{}; + sigh &, const entity_type)> update{}; +}; + + +/** + * @brief Defines the component-to-storage conversion. + * + * Formally: + * + * * If the component type is a non-const one, the member typedef type is the + * declared storage type. + * * If the component type is a const one, the member typedef type is the + * declared storage type, except it has a const-qualifier added. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Type of objects assigned to the entities. + */ +template +struct storage_traits { + /*! @brief Resulting type after component-to-storage conversion. */ + using storage_type = sigh_storage_mixin>; +}; + + +/** + * @brief Gets the element assigned to an entity from a storage, if any. + * @tparam Type Storage type. + * @param container A valid instance of a storage class. + * @param entity A valid entity identifier. + * @return A possibly empty tuple containing the requested element. + */ +template +[[nodiscard]] auto get_as_tuple([[maybe_unused]] Type &container, [[maybe_unused]] const typename Type::entity_type entity) { + static_assert(std::is_same_v, typename storage_traits::storage_type>, "Invalid storage"); + + if constexpr(std::is_void_v) { + return std::make_tuple(); + } else { + return std::forward_as_tuple(container.get(entity)); + } +} + + +} + + +#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 +struct exclude_t: type_list {}; + + +/** + * @brief Variable template for exclusion lists. + * @tparam Type List of types. + */ +template +inline constexpr exclude_t exclude{}; + + +/** + * @brief Alias for lists of observed components. + * @tparam Type List of types. + */ +template +struct get_t: type_list{}; + + +/** + * @brief Variable template for lists of observed components. + * @tparam Type List of types. + */ +template +inline constexpr get_t get{}; + + +} + + +#endif // #include "entity/view.hpp" +#ifndef ENTT_ENTITY_VIEW_HPP +#define ENTT_ENTITY_VIEW_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + +// #include "utility.hpp" + + + +namespace entt { + + +/** + * @brief View. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +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. + * + * @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 views. + * + * @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 Exclude Types of components used to filter the view. + * @tparam Component Types of components iterated by the view. + */ +template +class basic_view, Component...> final { + template + using storage_type = constness_as_t>::storage_type, Comp>; + + using unchecked_type = std::array *, (sizeof...(Component) - 1)>; + + template + class view_iterator final { + friend class basic_view, Component...>; + + view_iterator(It from, It to, It curr, unchecked_type other, const std::tuple *...> &ignore) ENTT_NOEXCEPT + : first{from}, + last{to}, + it{curr}, + unchecked{other}, + filter{ignore} + { + if(it != last && !valid()) { + ++(*this); + } + } + + [[nodiscard]] bool valid() const { + const auto entt = *it; + + return std::all_of(unchecked.cbegin(), unchecked.cend(), [entt](const basic_sparse_set *curr) { return curr->contains(entt); }) + && !(std::get *>(filter)->contains(entt) || ...); + } + + public: + using difference_type = typename std::iterator_traits::difference_type; + using value_type = typename std::iterator_traits::value_type; + using pointer = typename std::iterator_traits::pointer; + using reference = typename std::iterator_traits::reference; + using iterator_category = std::bidirectional_iterator_tag; + + view_iterator() ENTT_NOEXCEPT + : view_iterator{{}, {}, {}, {}, {}} + {} + + view_iterator & operator++() ENTT_NOEXCEPT { + while(++it != last && !valid()); + return *this; + } + + view_iterator operator++(int) ENTT_NOEXCEPT { + view_iterator orig = *this; + return ++(*this), orig; + } + + view_iterator & operator--() ENTT_NOEXCEPT { + while(--it != first && !valid()); + return *this; + } + + view_iterator operator--(int) ENTT_NOEXCEPT { + view_iterator orig = *this; + return operator--(), orig; + } + + [[nodiscard]] bool operator==(const view_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + [[nodiscard]] bool operator!=(const view_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + [[nodiscard]] pointer operator->() const { + return &*it; + } + + [[nodiscard]] reference operator*() const { + return *operator->(); + } + + private: + It first; + It last; + It it; + unchecked_type unchecked; + std::tuple *...> filter; + }; + + class iterable_view final { + friend class basic_view, Component...>; + + template + class iterable_view_iterator final { + friend class iterable_view; + + iterable_view_iterator(It from, const basic_view *parent) ENTT_NOEXCEPT + : it{from}, + view{parent} + {} + + public: + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); + using pointer = void; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + + iterable_view_iterator & operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + iterable_view_iterator operator++(int) ENTT_NOEXCEPT { + iterable_view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return std::tuple_cat(std::make_tuple(*it), view->get(*it)); + } + + [[nodiscard]] bool operator==(const iterable_view_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + [[nodiscard]] bool operator!=(const iterable_view_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + private: + It it; + const basic_view *view; + }; + + iterable_view(const basic_view &parent) + : view{parent} + {} + + public: + using iterator = iterable_view_iterator::iterator>>; + using reverse_iterator = iterable_view_iterator::reverse_iterator>>; + + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return { view.begin(), &view }; + } + + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return { view.end(), &view }; + } + + [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return { view.rbegin(), &view }; + } + + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return { view.rend(), &view }; + } + + private: + const basic_view view; + }; + + [[nodiscard]] const basic_sparse_set * candidate() const ENTT_NOEXCEPT { + return (std::min)({ static_cast *>(std::get *>(pools))... }, [](const auto *lhs, const auto *rhs) { + return lhs->size() < rhs->size(); + }); + } + + [[nodiscard]] unchecked_type unchecked(const basic_sparse_set *cpool) const { + std::size_t pos{}; + unchecked_type other{}; + (static_cast(std::get *>(pools) == cpool ? void() : void(other[pos++] = std::get *>(pools))), ...); + return other; + } + + template + [[nodiscard]] auto dispatch_get([[maybe_unused]] It &it, [[maybe_unused]] const Entity entt) const { + if constexpr(std::is_same_v::value_type, typename storage_type::value_type>) { + return std::forward_as_tuple(*it); + } else { + return get_as_tuple(*std::get *>(pools), entt); + } + } + + template + void traverse(Func func) const { + if constexpr(std::is_void_v *>(pools)->get({}))>) { + for(const auto entt: static_cast &>(*std::get *>(pools))) { + if(((std::is_same_v || std::get *>(pools)->contains(entt)) && ...) + && !(std::get *>(filter)->contains(entt) || ...)) + { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); + } else { + std::apply(func, get(entt)); + } + } + } + } else { + auto it = std::get *>(pools)->begin(); + + for(const auto entt: static_cast &>(*std::get *>(pools))) { + if(((std::is_same_v || std::get *>(pools)->contains(entt)) && ...) + && !(std::get *>(filter)->contains(entt) || ...)) + { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(it, entt)...)); + } else { + std::apply(func, std::tuple_cat(dispatch_get(it, entt)...)); + } + } + + ++it; + } + } + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Bidirectional iterator type. */ + using iterator = view_iterator::iterator>; + /*! @brief Reverse iterator type. */ + using reverse_iterator = view_iterator::reverse_iterator>; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() ENTT_NOEXCEPT + : 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, const storage_type &... epool) ENTT_NOEXCEPT + : pools{&component...}, + filter{&epool...}, + view{candidate()} + {} + + /** + * @brief Forces the type to use to drive iterations. + * @tparam Comp Type of component to use to drive the iteration. + */ + template + void use() const ENTT_NOEXCEPT { + view = std::get *>(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 { + return iterator{view->begin(), view->end(), view->begin(), unchecked(view), 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 { + return iterator{view->begin(), view->end(), view->end(), unchecked(view), filter}; + } + + /** + * @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 { + return reverse_iterator{view->rbegin(), view->rend(), view->rbegin(), unchecked(view), filter}; + } + + /** + * @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 { + return reverse_iterator{view->rbegin(), view->rend(), view->rend(), unchecked(view), 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 { + 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 { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid entity identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const { + const auto it = iterator{view->begin(), view->end(), view->find(entt), unchecked(view), filter}; + return (it != end() && *it == entt) ? it : end(); + } + + /** + * @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 entity identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const { + return (std::get *>(pools)->contains(entt) && ...) && !(std::get *>(filter)->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 view + * results in undefined behavior. + * + * @tparam Comp Types of components to get. + * @param entt A valid entity identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const { + ENTT_ASSERT(contains(entt)); + + if constexpr(sizeof...(Comp) == 0) { + return std::tuple_cat(get_as_tuple(*std::get *>(pools), entt)...); + } else if constexpr(sizeof...(Comp) == 1) { + return (std::get *>(pools)->get(entt), ...); + } else { + return std::tuple_cat(get_as_tuple(*std::get *>(pools), entt)...); + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.
+ * 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 + void each(Func func) const { + ((std::get *>(pools) == view ? traverse(std::move(func)) : void()), ...); + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The pool of the suggested component is used to lead the iterations. The + * returned entities will therefore respect the order of the pool associated + * with that type.
+ * It is no longer guaranteed that the performance is the best possible, but + * there will be greater control over the order of iteration. + * + * @sa each + * + * @tparam Comp Type of component to use to drive the iteration. + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + use(); + traverse(std::move(func)); + } + + /** + * @brief Returns an iterable object to use to _visit_ the view. + * + * 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 view. + */ + [[nodiscard]] iterable_view each() const ENTT_NOEXCEPT { + return iterable_view{*this}; + } + + /** + * @brief Returns an iterable object to use to _visit_ the view. + * + * The pool of the suggested component is used to lead the iterations. The + * returned elements will therefore respect the order of the pool associated + * with that type.
+ * It is no longer guaranteed that the performance is the best possible, but + * there will be greater control over the order of iteration. + * + * @sa each + * + * @tparam Comp Type of component to use to drive the iteration. + * @return An iterable object to use to _visit_ the view. + */ + template + [[nodiscard]] iterable_view each() const ENTT_NOEXCEPT { + use(); + return iterable_view{*this}; + } + + /** + * @brief Combines two views in a _more specific_ one (friend function). + * @tparam Id A valid entity type (see entt_traits for more details). + * @tparam ELhs Filter list of the first view. + * @tparam CLhs Component list of the first view. + * @tparam ERhs Filter list of the second view. + * @tparam CRhs Component list of the second view. + * @return A more specific view. + */ + template + friend auto operator|(const basic_view, CLhs...> &, const basic_view, CRhs...> &); + +private: + const std::tuple *...> pools; + const std::tuple *...> filter; + mutable const basic_sparse_set *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. + * + * @note + * Views share a reference to the underlying data structure 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 views. + * + * @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 Component Type of component iterated by the view. + */ +template +class basic_view, Component> final { + using storage_type = constness_as_t>::storage_type, Component>; + + class iterable_view { + friend class basic_view, Component>; + + template + class iterable_view_iterator { + friend class iterable_view; + + template + iterable_view_iterator(It... from, Discard...) ENTT_NOEXCEPT + : it{from...} + {} + + public: + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); + using pointer = void; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + + iterable_view_iterator & operator++() ENTT_NOEXCEPT { + return (++std::get(it), ...), *this; + } + + iterable_view_iterator operator++(int) ENTT_NOEXCEPT { + iterable_view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return { *std::get(it)... }; + } + + [[nodiscard]] bool operator==(const iterable_view_iterator &other) const ENTT_NOEXCEPT { + return std::get<0>(other.it) == std::get<0>(it); + } + + [[nodiscard]] bool operator!=(const iterable_view_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + private: + std::tuple it; + }; + + iterable_view(storage_type &ref) + : pool{&ref} + {} + + public: + using iterator = std::conditional_t< + std::is_void_v().get({}))>, + iterable_view_iterator::iterator>, + iterable_view_iterator::iterator, decltype(std::declval().begin())> + >; + using reverse_iterator = std::conditional_t< + std::is_void_v().get({}))>, + iterable_view_iterator::reverse_iterator>, + iterable_view_iterator::reverse_iterator, decltype(std::declval().rbegin())> + >; + + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return iterator{pool->basic_sparse_set::begin(), pool->begin()}; + } + + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return iterator{pool->basic_sparse_set::end(), pool->end()}; + } + + [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return reverse_iterator{pool->basic_sparse_set::rbegin(), pool->rbegin()}; + } + + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return reverse_iterator{pool->basic_sparse_set::rend(), pool->rend()}; + } + + private: + storage_type * const pool; + }; + +public: + /*! @brief Type of component iterated by the view. */ + using raw_type = Component; + /*! @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::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename basic_sparse_set::reverse_iterator; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() ENTT_NOEXCEPT + : pools{}, + filter{} + {} + + /** + * @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{} + {} + + /** + * @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 std::get<0>(pools)->size(); + } + + /** + * @brief Checks whether a view is empty. + * @return True if the view is empty, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return std::get<0>(pools)->empty(); + } + + /** + * @brief Direct access to the list of components. + * + * The returned pointer is such that range `[raw(), raw() + size())` is + * always a valid range, even if the container is empty. + * + * @return A pointer to the array of components. + */ + [[nodiscard]] raw_type * raw() const ENTT_NOEXCEPT { + return std::get<0>(pools)->raw(); + } + + /** + * @brief Direct access to the list of entities. + * + * The returned pointer is such that range `[data(), data() + size())` is + * always a valid range, even if the container is empty. + * + * @return A pointer to the array of entities. + */ + [[nodiscard]] const entity_type * data() const ENTT_NOEXCEPT { + return std::get<0>(pools)->data(); + } + + /** + * @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 std::get<0>(pools)->basic_sparse_set::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 std::get<0>(pools)->basic_sparse_set::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 std::get<0>(pools)->basic_sparse_set::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 std::get<0>(pools)->basic_sparse_set::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 { + 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 { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid entity identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const { + const auto it = std::get<0>(pools)->find(entt); + 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 view is properly initialized. + * @return True if the view is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return std::get<0>(pools) != nullptr; + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid entity identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const { + return std::get<0>(pools)->contains(entt); + } + + /** + * @brief Returns the component 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 view + * results in undefined behavior. + * + * @tparam Comp Types of components to get. + * @param entt A valid entity identifier. + * @return The component assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + ENTT_ASSERT(contains(entt)); + + if constexpr(sizeof...(Comp) == 0) { + return get_as_tuple(*std::get<0>(pools), entt); + } else { + static_assert(std::is_same_v, "Invalid component type"); + 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.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Component &); + * void(Component &); + * @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 + void each(Func func) const { + if constexpr(std::is_void_v(pools)->get({}))>) { + if constexpr(std::is_invocable_v) { + for(auto pos = size(); pos; --pos) { + func(); + } + } else { + for(auto entity: *this) { + func(entity); + } + } + } else { + if constexpr(is_applicable_v) { + for(const auto pack: each()) { + std::apply(func, pack); + } + } else { + for(auto &&component: *std::get<0>(pools)) { + func(component); + } + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ the view. + * + * The iterable object returns tuples that contain the current entity and a + * reference to its component if it's a non-empty one. The _constness_ of + * the component 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 view. + */ + [[nodiscard]] iterable_view each() const ENTT_NOEXCEPT { + return iterable_view{*std::get<0>(pools)}; + } + + /** + * @brief Combines two views in a _more specific_ one (friend function). + * @tparam Id A valid entity type (see entt_traits for more details). + * @tparam ELhs Filter list of the first view. + * @tparam CLhs Component list of the first view. + * @tparam ERhs Filter list of the second view. + * @tparam CRhs Component list of the second view. + * @return A more specific view. + */ + template + friend auto operator|(const basic_view, CLhs...> &, const basic_view, CRhs...> &); + +private: + const std::tuple pools; + const std::tuple<> filter; +}; + + +/** + * @brief Deduction guide. + * @tparam Storage Type of storage classes used to create the view. + * @param storage The storage for the types to iterate. + */ +template +basic_view(Storage &... storage) +-> basic_view, entt::exclude_t<>, constness_as_t...>; + + +/** + * @brief Combines two views in a _more specific_ one. + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam ELhs Filter list of the first view. + * @tparam CLhs Component list of the first view. + * @tparam ERhs Filter list of the second view. + * @tparam CRhs Component list of the second view. + * @param lhs A valid reference to the first view. + * @param rhs A valid reference to the second view. + * @return A more specific view. + */ +template +[[nodiscard]] auto operator|(const basic_view, CLhs...> &lhs, const basic_view, CRhs...> &rhs) { + using view_type = basic_view, CLhs..., CRhs...>; + return std::apply([](auto *... storage) { return view_type{*storage...}; }, std::tuple_cat(lhs.pools, rhs.pools, lhs.filter, rhs.filter)); +} + + +} + + +#endif // #include "locator/locator.hpp" #ifndef ENTT_LOCATOR_LOCATOR_HPP @@ -10965,16 +25360,6 @@ private: #endif -#ifndef ENTT_HS_SUFFIX -# define ENTT_HS_SUFFIX _hs -#endif - - -#ifndef ENTT_HWS_SUFFIX -# define ENTT_HWS_SUFFIX _hws -#endif - - #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else @@ -10989,12 +25374,17 @@ private: #endif -#ifndef ENTT_PAGE_SIZE -# define ENTT_PAGE_SIZE 32768 +#ifdef ENTT_PAGE_SIZE +static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); +#else +# define ENTT_PAGE_SIZE 4096 #endif -#ifndef ENTT_ASSERT +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition) assert(condition) #endif @@ -11010,17 +25400,11 @@ private: #ifndef ENTT_STANDARD_CPP -# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) -# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' -# elif 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_CONSTEXPR +# elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' @@ -11028,13 +25412,6 @@ private: #endif -#ifndef ENTT_STANDALONE -# define ENTT_FAST_PATH(...) false -#else -# define ENTT_FAST_PATH(Cond) Cond -#endif - - #endif @@ -11140,6 +25517,48 @@ private: } +#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 +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 +struct adl_meta_pointer_like { + /** + * @brief Uses the default ADL based lookup method to resolve the call. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ + static decltype(auto) dereference(const Type &value) { + return dereference_meta_pointer_like(value); + } +}; + + +} + + #endif // #include "meta/container.hpp" @@ -11150,6 +25569,7 @@ private: #include #include #include +#include #include #include #include @@ -11164,16 +25584,6 @@ private: #endif -#ifndef ENTT_HS_SUFFIX -# define ENTT_HS_SUFFIX _hs -#endif - - -#ifndef ENTT_HWS_SUFFIX -# define ENTT_HWS_SUFFIX _hws -#endif - - #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else @@ -11188,12 +25598,17 @@ private: #endif -#ifndef ENTT_PAGE_SIZE -# define ENTT_PAGE_SIZE 32768 +#ifdef ENTT_PAGE_SIZE +static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); +#else +# define ENTT_PAGE_SIZE 4096 #endif -#ifndef ENTT_ASSERT +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition) assert(condition) #endif @@ -11209,17 +25624,11 @@ private: #ifndef ENTT_STANDARD_CPP -# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) -# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' -# elif 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_CONSTEXPR +# elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' @@ -11227,12 +25636,744 @@ private: #endif -#ifndef ENTT_STANDALONE -# define ENTT_FAST_PATH(...) false -#else -# define ENTT_FAST_PATH(Cond) Cond #endif +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + + +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifdef ENTT_PAGE_SIZE +static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); +#else +# define ENTT_PAGE_SIZE 4096 +#endif + + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +#include +// #include "../config/config.h" + + + +namespace entt { + + +template)> +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; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // Unfortunately, doxygen cannot parse such a construct. + /*! @cond TURN_OFF_DOXYGEN */ + : choice_t + /*! @endcond */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t 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 +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::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 +struct size_of: std::integral_constant {}; + + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr auto size_of_v = size_of::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 +using unpack_as_t = Type; + + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_v = Value; + + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +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 +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 +struct type_list_element> + : type_list_element> +{}; + + +/** + * @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 +struct type_list_element<0u, type_list> { + /*! @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 +using type_list_element_t = typename type_list_element::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 +constexpr type_list operator+(type_list, type_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + std::disjunction_v...>, + typename type_list_unique>::type, + type_list_cat_t, typename type_list_unique>::type> + >; +}; + + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = type_list<>; +}; + + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::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 +struct type_list_contains; + + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other>: std::disjunction...> {}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr auto type_list_contains_v = type_list_contains::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +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 +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 +struct value_list_element> + : value_list_element> +{}; + + +/** + * @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 +struct value_list_element<0u, value_list> { + /*! @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 +inline constexpr auto value_list_element_v = value_list_element::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 +constexpr value_list operator+(value_list, value_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +[[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) +-> decltype(std::declval() == std::declval()) { return true; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>); +} + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template +struct is_equality_comparable: std::bool_constant(choice<2>)> {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** +* @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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr auto is_applicable_v = is_applicable::value; + + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +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 +struct is_applicable_r>: std::is_invocable_r {}; + + +/** + * @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 +inline constexpr auto is_applicable_r_v = is_applicable_r::value; + + +/** +* @brief Provides the member constant `value` to true if a given type is +* complete, false otherwise. +* @tparam Type Potential complete type. +*/ +template +struct is_complete: std::false_type {}; + + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + + +/** +* @brief Helper variable template. +* @tparam Type Potential complete type. +*/ +template +inline constexpr auto is_complete_v = is_complete::value; + + +/** + * @brief Provides the member constant `value` to true if a given type is empty + * and the empty type optimization is enabled, false otherwise. + * @tparam Type Potential empty type. + */ +template +struct is_empty: ENTT_IS_EMPTY(Type) {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potential empty type. + */ +template +inline constexpr auto is_empty_v = is_empty::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 +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::add_const_t; +}; + + +/** + * @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 +using constness_as_t = typename constness_as::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 +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class * clazz(Ret(Class:: *)(Args...)); + + template + static Class * clazz(Ret(Class:: *)(Args...) const); + + template + static Class * clazz(Type Class:: *); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + + +} + #endif @@ -11247,6 +26388,14 @@ private: namespace entt { +/** + * @brief Traits class template to be specialized to enable support for meta + * template information. + */ +template +struct meta_template_traits; + + /** * @brief Traits class template to be specialized to enable support for meta * sequence containers. @@ -11263,54 +26412,6 @@ template struct meta_associative_container_traits; -/** - * @brief Provides the member constant `value` to true if support for meta - * sequence containers is enabled for the given type, false otherwise. - * @tparam Type Potentially sequence container type. - */ -template -struct has_meta_sequence_container_traits: std::false_type {}; - - -/*! @copydoc has_meta_sequence_container_traits */ -template -struct has_meta_sequence_container_traits::value_type>> - : std::true_type -{}; - - -/** - * @brief Helper variable template. - * @tparam Type Potentially sequence container type. - */ -template -inline constexpr auto has_meta_sequence_container_traits_v = has_meta_sequence_container_traits::value; - - -/** - * @brief Provides the member constant `value` to true if support for meta - * associative containers is enabled for the given type, false otherwise. - * @tparam Type Potentially associative container type. - */ -template -struct has_meta_associative_container_traits: std::false_type {}; - - -/*! @copydoc has_meta_associative_container_traits */ -template -struct has_meta_associative_container_traits::key_type>> - : std::true_type -{}; - - -/** - * @brief Helper variable template. - * @tparam Type Potentially associative container type. - */ -template -inline constexpr auto has_meta_associative_container_traits_v = has_meta_associative_container_traits::value; - - /** * @brief Provides the member constant `value` to true if a meta associative * container claims to wrap a key-only type, false otherwise. @@ -11322,8 +26423,8 @@ struct is_key_only_meta_associative_container: std::true_type {}; /*! @copydoc is_key_only_meta_associative_container */ template -struct is_key_only_meta_associative_container::mapped_type>> - : std::false_type +struct is_key_only_meta_associative_container::type::mapped_type>> + : std::false_type {}; @@ -11344,6 +26445,15 @@ template 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 +struct is_meta_pointer_like: is_meta_pointer_like {}; + + /** * @brief Helper variable template. * @tparam Type Potentially pointer-like type. @@ -11362,11 +26472,16 @@ inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like::value namespace entt { -namespace internal { - - +/** + * @brief Container traits. + * @tparam Container Type of the underlying container. + * @tparam Trait Traits associated with the underlying container. + */ template class... Trait> -struct container_traits: public Trait... {}; +struct meta_container_traits: public Trait... { + /*! @brief Type of container. */ + using type = Container; +}; /** @@ -11375,19 +26490,12 @@ struct container_traits: public Trait... {}; */ template struct basic_container { - /*! @brief Iterator type of the container. */ - using iterator = typename Container::iterator; - /*! @brief Unsigned integer type. */ - using size_type = typename Container::size_type; - /*! @brief Value type of the container. */ - using value_type = typename Container::value_type; - /** * @brief Returns the size of the given container. * @param cont The container for which to return the size. * @return The size of the given container. */ - [[nodiscard]] static size_type size(const Container &cont) ENTT_NOEXCEPT { + [[nodiscard]] static typename Container::size_type size(const Container &cont) ENTT_NOEXCEPT { return cont.size(); } @@ -11396,7 +26504,16 @@ struct basic_container { * @param cont The container for which to return the iterator. * @return An iterator to the first element of the given container. */ - [[nodiscard]] static iterator begin(Container &cont) { + [[nodiscard]] static typename Container::iterator begin(Container &cont) { + return cont.begin(); + } + + /** + * @brief Returns an iterator to the first element of the given container. + * @param cont The container for which to return the iterator. + * @return An iterator to the first element of the given container. + */ + [[nodiscard]] static typename Container::const_iterator cbegin(const Container &cont) { return cont.begin(); } @@ -11405,7 +26522,16 @@ struct basic_container { * @param cont The container for which to return the iterator. * @return An iterator past the last element of the given container. */ - [[nodiscard]] static iterator end(Container &cont) { + [[nodiscard]] static typename Container::iterator end(Container &cont) { + return cont.end(); + } + + /** + * @brief Returns an iterator past the last element of the given container. + * @param cont The container for which to return the iterator. + * @return An iterator past the last element of the given container. + */ + [[nodiscard]] static typename Container::const_iterator cend(const Container &cont) { return cont.end(); } }; @@ -11417,17 +26543,19 @@ struct basic_container { */ template struct basic_associative_container { - /*! @brief Key type of the sequence container. */ - using key_type = typename Container::key_type; - /** - * @brief Returns an iterator to the element with key equivalent to the given - * one, if any. + * @brief Returns an iterator to the element with key equivalent to the + * given one, if any. * @param cont The container in which to search for the element. * @param key The key of the element to search. * @return An iterator to the element with the given key, if any. */ - [[nodiscard]] static typename Container::iterator find(Container &cont, const key_type &key) { + [[nodiscard]] static typename Container::iterator find(Container &cont, const typename Container::key_type &key) { + return cont.find(key); + } + + /*! @copydoc find */ + [[nodiscard]] static typename Container::const_iterator cfind(const Container &cont, const typename Container::key_type &key) { return cont.find(key); } }; @@ -11444,7 +26572,7 @@ struct basic_dynamic_container { * @param cont The container for which to clear the content. * @return True in case of success, false otherwise. */ - [[nodiscard]] static bool clear(Container &cont) { + [[nodiscard]] static bool clear([[maybe_unused]] Container &cont) { return cont.clear(), true; } }; @@ -11462,7 +26590,7 @@ struct basic_dynamic_associative_container { * @param key The element to remove. * @return A bool denoting whether the removal took place. */ - [[nodiscard]] static bool erase(Container &cont, const typename Container::key_type &key) { + [[nodiscard]] static bool erase([[maybe_unused]] Container &cont, [[maybe_unused]] const typename Container::key_type &key) { const auto sz = cont.size(); return cont.erase(key) != sz; } @@ -11476,13 +26604,18 @@ struct basic_dynamic_associative_container { template struct basic_sequence_container { /** - * @brief Returns a reference to the element at the specified location of the - * given container (no bounds checking is performed). + * @brief Returns a reference to the element at the specified location of + * the given container (no bounds checking is performed). * @param cont The container from which to get the element. * @param pos The position of the element to return. * @return A reference to the requested element. */ - [[nodiscard]] static typename Container::value_type & get(Container &cont, typename Container::size_type pos) { + [[nodiscard]] static typename Container::reference get(Container &cont, typename Container::size_type pos) { + return cont[pos]; + } + + /*! @copydoc get */ + [[nodiscard]] static typename Container::const_reference cget(const Container &cont, typename Container::size_type pos) { return cont[pos]; } }; @@ -11500,7 +26633,7 @@ struct dynamic_associative_key_only_container { * @param key The element to insert. * @return A bool denoting whether the insertion took place. */ - [[nodiscard]] static bool insert(Container &cont, const typename Container::key_type &key) { + [[nodiscard]] static bool insert([[maybe_unused]] Container &cont, [[maybe_unused]] const typename Container::key_type &key) { return cont.insert(key).second; } }; @@ -11519,7 +26652,7 @@ struct dynamic_associative_key_value_container { * @param value The value of the element to insert. * @return A bool denoting whether the insertion took place. */ - [[nodiscard]] static bool insert(Container &cont, const typename Container::key_type &key, const typename Container::mapped_type &value) { + [[nodiscard]] static bool insert([[maybe_unused]] Container &cont, [[maybe_unused]] const typename Container::key_type &key, [[maybe_unused]] const typename Container::mapped_type &value) { return cont.insert(std::make_pair(key, value)).second; } }; @@ -11532,36 +26665,39 @@ struct dynamic_associative_key_value_container { template struct dynamic_sequence_container { /** - * @brief Resizes the given container to contain the given number of elements. + * @brief Resizes the given container to contain the given number of + * elements. * @param cont The container to resize. * @param sz The new size of the container. * @return True in case of success, false otherwise. */ - [[nodiscard]] static bool resize(Container &cont, typename Container::size_type sz) { - return (cont.resize(sz), true); + [[nodiscard]] static bool resize([[maybe_unused]] Container &cont, [[maybe_unused]] typename Container::size_type sz) { + return cont.resize(sz), true; } /** - * @brief Inserts an element at the specified location of the given container. + * @brief Inserts an element at the specified location of the given + * container. * @param cont The container into which to insert the element. * @param it Iterator before which the element will be inserted. * @param value Element value to insert. * @return A pair consisting of an iterator to the inserted element (in case * of success) and a bool denoting whether the insertion took place. */ - [[nodiscard]] static std::pair insert(Container &cont, typename Container::iterator it, const typename Container::value_type &value) { + [[nodiscard]] static std::pair insert([[maybe_unused]] Container &cont, [[maybe_unused]] typename Container::const_iterator it, [[maybe_unused]] const typename Container::value_type &value) { return { cont.insert(it, value), true }; } /** - * @brief Removes the element at the specified location from the given container. + * @brief Removes the element at the specified location from the given + * container. * @param cont The container from which to remove the element. * @param it Iterator to the element to remove. * @return A pair consisting of an iterator following the last removed * element (in case of success) and a bool denoting whether the insertion * took place. */ - [[nodiscard]] static std::pair erase(Container &cont, typename Container::iterator it) { + [[nodiscard]] static std::pair erase([[maybe_unused]] Container &cont, [[maybe_unused]] typename Container::const_iterator it) { return { cont.erase(it), true }; } }; @@ -11594,7 +26730,7 @@ struct fixed_sequence_container { * @return A pair consisting of an invalid iterator and a false value to * indicate failure in all cases. */ - [[nodiscard]] static std::pair insert(const Container &, typename Container::iterator, const typename Container::value_type &) { + [[nodiscard]] static std::pair insert(const Container &, typename Container::const_iterator, const typename Container::value_type &) { return { {}, false }; } @@ -11603,15 +26739,12 @@ struct fixed_sequence_container { * @return A pair consisting of an invalid iterator and a false value to * indicate failure in all cases. */ - [[nodiscard]] static std::pair erase(const Container &, typename Container::iterator) { + [[nodiscard]] static std::pair erase(const Container &, typename Container::const_iterator) { return { {}, false }; } }; -} - - /** * @brief Meta sequence container traits for `std::vector`s of any type. * @tparam Type The type of elements. @@ -11619,13 +26752,13 @@ struct fixed_sequence_container { */ template struct meta_sequence_container_traits> - : internal::container_traits< - std::vector, - internal::basic_container, - internal::basic_dynamic_container, - internal::basic_sequence_container, - internal::dynamic_sequence_container - > + : meta_container_traits< + std::vector, + basic_container, + basic_dynamic_container, + basic_sequence_container, + dynamic_sequence_container + > {}; @@ -11636,12 +26769,12 @@ struct meta_sequence_container_traits> */ template struct meta_sequence_container_traits> - : internal::container_traits< - std::array, - internal::basic_container, - internal::basic_sequence_container, - internal::fixed_sequence_container - > + : meta_container_traits< + std::array, + basic_container, + basic_sequence_container, + fixed_sequence_container + > {}; @@ -11653,18 +26786,15 @@ struct meta_sequence_container_traits> */ template struct meta_associative_container_traits> - : internal::container_traits< - std::map, - internal::basic_container, - internal::basic_associative_container, - internal::basic_dynamic_container, - internal::basic_dynamic_associative_container, - internal::dynamic_associative_key_value_container - > -{ - /*! @brief Mapped type of the sequence container. */ - using mapped_type = typename std::map::mapped_type; -}; + : meta_container_traits< + std::map, + basic_container, + basic_associative_container, + basic_dynamic_container, + basic_dynamic_associative_container, + dynamic_associative_key_value_container + > +{}; /** @@ -11676,18 +26806,15 @@ struct meta_associative_container_traits> */ template struct meta_associative_container_traits> - : internal::container_traits< - std::unordered_map, - internal::basic_container, - internal::basic_associative_container, - internal::basic_dynamic_container, - internal::basic_dynamic_associative_container, - internal::dynamic_associative_key_value_container - > -{ - /*! @brief Mapped type of the sequence container. */ - using mapped_type = typename std::unordered_map::mapped_type; -}; + : meta_container_traits< + std::unordered_map, + basic_container, + basic_associative_container, + basic_dynamic_container, + basic_dynamic_associative_container, + dynamic_associative_key_value_container + > +{}; /** @@ -11697,14 +26824,14 @@ struct meta_associative_container_traits */ template struct meta_associative_container_traits> - : internal::container_traits< - std::set, - internal::basic_container, - internal::basic_associative_container, - internal::basic_dynamic_container, - internal::basic_dynamic_associative_container, - internal::dynamic_associative_key_only_container - > + : meta_container_traits< + std::set, + basic_container, + basic_associative_container, + basic_dynamic_container, + basic_dynamic_associative_container, + dynamic_associative_key_only_container + > {}; @@ -11716,14 +26843,14 @@ struct meta_associative_container_traits> */ template struct meta_associative_container_traits> - : internal::container_traits< - std::unordered_set, - internal::basic_container, - internal::basic_associative_container, - internal::basic_dynamic_container, - internal::basic_dynamic_associative_container, - internal::dynamic_associative_key_only_container - > + : meta_container_traits< + std::unordered_set, + basic_container, + basic_associative_container, + basic_dynamic_container, + basic_dynamic_associative_container, + dynamic_associative_key_only_container + > {}; @@ -11842,9 +26969,7 @@ private: #define ENTT_META_FACTORY_HPP -#include #include -#include #include #include #include @@ -11855,97 +26980,26 @@ private: #define ENTT_CORE_FWD_HPP +#include // #include "../config/config.h" -#ifndef ENTT_CONFIG_CONFIG_H -#define ENTT_CONFIG_CONFIG_H - - -#ifndef ENTT_NOEXCEPT -# define ENTT_NOEXCEPT noexcept -#endif - - -#ifndef ENTT_HS_SUFFIX -# define ENTT_HS_SUFFIX _hs -#endif - - -#ifndef ENTT_HWS_SUFFIX -# define ENTT_HWS_SUFFIX _hws -#endif - - -#ifndef ENTT_USE_ATOMIC -# define ENTT_MAYBE_ATOMIC(Type) Type -#else -# include -# define ENTT_MAYBE_ATOMIC(Type) std::atomic -#endif - - -#ifndef ENTT_ID_TYPE -# include -# define ENTT_ID_TYPE std::uint32_t -#endif - - -#ifndef ENTT_PAGE_SIZE -# define ENTT_PAGE_SIZE 32768 -#endif - - -#ifndef ENTT_ASSERT -# include -# define ENTT_ASSERT(condition) assert(condition) -#endif - - -#ifndef ENTT_NO_ETO -# include -# define ENTT_IS_EMPTY(Type) std::is_empty -#else -# include -# define ENTT_IS_EMPTY(Type) std::false_type -#endif - - -#ifndef ENTT_STANDARD_CPP -# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) -# define ENTT_PRETTY_FUNCTION_CONSTEXPR -# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ -# define ENTT_PRETTY_FUNCTION_PREFIX '=' -# define ENTT_PRETTY_FUNCTION_SUFFIX ']' -# elif 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_CONSTEXPR -# define ENTT_PRETTY_FUNCTION __FUNCSIG__ -# define ENTT_PRETTY_FUNCTION_PREFIX '<' -# define ENTT_PRETTY_FUNCTION_SUFFIX '>' -# endif -#endif - - -#ifndef ENTT_STANDALONE -# define ENTT_FAST_PATH(...) false -#else -# define ENTT_FAST_PATH(Cond) Cond -#endif - - -#endif namespace entt { +template)> +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; + + } @@ -11957,6 +27011,7 @@ using id_type = ENTT_ID_TYPE; #include +#include // #include "../config/config.h" // #include "../core/attribute.h" @@ -12086,6 +27141,18 @@ public: /*! @brief Unsigned integer type. */ using hash_type = id_type; + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { + id_type partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + /** * @brief Returns directly the numeric representation of a string. * @@ -12115,18 +27182,6 @@ public: return helper(wrapper.str); } - /** - * @brief Returns directly the numeric representation of a string view. - * @param str Human-readable identifer. - * @param size Length of the string to hash. - * @return The numeric representation of the string. - */ - [[nodiscard]] static hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { - id_type partial{traits_type::offset}; - while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } - return partial; - } - /*! @brief Constructs an empty hashed string. */ constexpr basic_hashed_string() ENTT_NOEXCEPT : str{nullptr}, hash{} @@ -12211,7 +27266,7 @@ private: * @param str Human-readable identifer. */ template -basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; @@ -12236,7 +27291,7 @@ using hashed_string = basic_hashed_string; using hashed_wstring = basic_hashed_string; -} +inline namespace literals { /** @@ -12244,7 +27299,7 @@ using hashed_wstring = basic_hashed_string; * @param str The literal without its suffix. * @return A properly initialized hashed string. */ -[[nodiscard]] constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT { +[[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_string{str}; } @@ -12254,11 +27309,17 @@ using hashed_wstring = basic_hashed_string; * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ -[[nodiscard]] constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { +[[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_wstring{str}; } +} + + +} + + #endif // #include "fwd.hpp" @@ -12277,7 +27338,7 @@ namespace entt { namespace internal { -struct ENTT_API type_index { +struct ENTT_API type_seq final { [[nodiscard]] static id_type next() ENTT_NOEXCEPT { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; @@ -12286,18 +27347,49 @@ struct ENTT_API type_index { template -[[nodiscard]] constexpr auto type_name() ENTT_NOEXCEPT { +[[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{}; + return std::string_view{""}; #endif } +template().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + + +template +[[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()); + return value; +} + + } @@ -12308,98 +27400,478 @@ template /** - * @brief Type index. + * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template -struct ENTT_API type_index { +struct ENTT_API type_seq final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() ENTT_NOEXCEPT { - static const id_type value = internal::type_index::next(); + static const id_type value = internal::type_seq::next(); return value; } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** - * @brief Provides the member constant `value` to true if a given type is - * indexable, false otherwise. - * @tparam Type Potentially indexable type. - */ -template -struct has_type_index: std::false_type {}; - - -/*! @brief has_type_index */ -template -struct has_type_index::value())>>: std::true_type {}; - - -/** - * @brief Helper variable template. - * @tparam Type Potentially indexable type. - */ -template -inline constexpr bool has_type_index_v = has_type_index::value; - - -/** - * @brief Type info. - * @tparam Type Type for which to generate information. + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. */ template -struct type_info { +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_CONSTEXPR - [[nodiscard]] static constexpr id_type id() ENTT_NOEXCEPT { - constexpr auto value = hashed_string::value(ENTT_PRETTY_FUNCTION); - return value; - } -#elif defined ENTT_PRETTY_FUNCTION - [[nodiscard]] static id_type id() ENTT_NOEXCEPT { - static const auto value = hashed_string::value(ENTT_PRETTY_FUNCTION); - return value; - } +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return internal::type_hash(0); #else - [[nodiscard]] static id_type id() ENTT_NOEXCEPT { - return type_index::value(); - } + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_seq::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 +struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ -#if defined ENTT_PRETTY_FUNCTION_CONSTEXPR - [[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT { - constexpr auto value = internal::type_name(); - return value; + [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { + return internal::type_name(0); } -#elif defined ENTT_PRETTY_FUNCTION - [[nodiscard]] static std::string_view name() ENTT_NOEXCEPT { - static const auto value = internal::type_name(); - return value; - } -#else - [[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT { - return internal::type_name(); - } -#endif + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } }; +/*! @brief Implementation specific information about a type. */ +class type_info final { + template + friend type_info type_id() ENTT_NOEXCEPT; + + type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT + : seq_value{seq_v}, + hash_value{hash_v}, + name_value{name_v} + {} + +public: + /*! @brief Default constructor. */ + type_info() ENTT_NOEXCEPT + : type_info({}, {}, {}) + {} + + /*! @brief Default copy constructor. */ + type_info(const type_info &) ENTT_NOEXCEPT = default; + /*! @brief Default move constructor. */ + type_info(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Default copy assignment operator. + * @return This type info object. + */ + type_info & operator=(const type_info &) ENTT_NOEXCEPT = default; + + /** + * @brief Default move assignment operator. + * @return This type info object. + */ + type_info & operator=(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Checks if a type info object is properly initialized. + * @return True if the object is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return name_value.data() != nullptr; + } + + /** + * @brief Type sequential identifier. + * @return Type sequential identifier. + */ + [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { + return seq_value; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { + return hash_value; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { + return name_value; + } + + /** + * @brief Compares the contents of two type info objects. + * @param other Object with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const type_info &other) const ENTT_NOEXCEPT { + return hash_value == other.hash_value; + } + +private: + id_type seq_value; + id_type hash_value; + std::string_view name_value; +}; + + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two contents differ, false otherwise. + */ +[[nodiscard]] inline bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/** + * @brief Returns the type info object for a given type. + * @tparam Type Type for which to generate a type info object. + * @return The type info object for the given type. + */ +template +[[nodiscard]] type_info type_id() ENTT_NOEXCEPT { + return type_info{ + type_seq>>::value(), + type_hash>>::value(), + type_name>>::value() + }; +} + + } #endif // #include "../core/type_traits.hpp" + +// #include "meta.hpp" +#ifndef ENTT_META_META_HPP +#define ENTT_META_META_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "hashed_string.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct ENTT_API type_seq final { + [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + + +template +[[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().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + + +template +[[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()); + return value; +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_seq final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() ENTT_NOEXCEPT { + static const id_type value = internal::type_seq::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } +}; + + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +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(0); +#else + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_seq::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 +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(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } +}; + + +/*! @brief Implementation specific information about a type. */ +class type_info final { + template + friend type_info type_id() ENTT_NOEXCEPT; + + type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT + : seq_value{seq_v}, + hash_value{hash_v}, + name_value{name_v} + {} + +public: + /*! @brief Default constructor. */ + type_info() ENTT_NOEXCEPT + : type_info({}, {}, {}) + {} + + /*! @brief Default copy constructor. */ + type_info(const type_info &) ENTT_NOEXCEPT = default; + /*! @brief Default move constructor. */ + type_info(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Default copy assignment operator. + * @return This type info object. + */ + type_info & operator=(const type_info &) ENTT_NOEXCEPT = default; + + /** + * @brief Default move assignment operator. + * @return This type info object. + */ + type_info & operator=(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Checks if a type info object is properly initialized. + * @return True if the object is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return name_value.data() != nullptr; + } + + /** + * @brief Type sequential identifier. + * @return Type sequential identifier. + */ + [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { + return seq_value; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { + return hash_value; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { + return name_value; + } + + /** + * @brief Compares the contents of two type info objects. + * @param other Object with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const type_info &other) const ENTT_NOEXCEPT { + return hash_value == other.hash_value; + } + +private: + id_type seq_value; + id_type hash_value; + std::string_view name_value; +}; + + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two contents differ, false otherwise. + */ +[[nodiscard]] inline bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/** + * @brief Returns the type info object for a given type. + * @tparam Type Type for which to generate a type info object. + * @return The type info object for the given type. + */ +template +[[nodiscard]] type_info type_id() ENTT_NOEXCEPT { + return type_info{ + type_seq>>::value(), + type_hash>>::value(), + type_name>>::value() + }; +} + + +} + + +#endif + +// #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP @@ -12416,6 +27888,79 @@ struct type_info { namespace entt { +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // Unfortunately, doxygen cannot parse such a construct. + /*! @cond TURN_OFF_DOXYGEN */ + : choice_t + /*! @endcond */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t 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 +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::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 +struct size_of: std::integral_constant {}; + + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr auto size_of_v = size_of::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. @@ -12443,7 +27988,7 @@ using integral_constant = std::integral_constant; /** - * @brief Alias template to ease the creation of named values. + * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template @@ -12451,57 +27996,64 @@ using tag = integral_constant; /** - * @brief Utility class to disambiguate overloaded functions. - * @tparam N Number of choices available. - */ -template -struct choice_t - // Unfortunately, doxygen cannot parse such a construct. - /*! @cond TURN_OFF_DOXYGEN */ - : choice_t - /*! @endcond */ -{}; - - -/*! @copybrief choice_t */ -template<> -struct choice_t<0> {}; - - -/** - * @brief Variable template for the choice trick. - * @tparam N Number of choices available. - */ -template -inline constexpr choice_t choice{}; - - -/*! @brief A class to use to push around lists of types, nothing more. */ -template -struct type_list {}; - - -/*! @brief Primary template isn't defined on purpose. */ -template -struct type_list_size; - - -/** - * @brief Compile-time number of elements in a type list. + * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template -struct type_list_size> - : std::integral_constant +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 +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 +struct type_list_element> + : type_list_element> {}; /** - * @brief Helper variable template. - * @tparam List Type list. + * @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 -inline constexpr auto type_list_size_v = type_list_size::value; +template +struct type_list_element<0u, type_list> { + /*! @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 +using type_list_element_t = typename type_list_element::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 +constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ @@ -12586,20 +28138,213 @@ template using type_list_unique_t = typename type_list_unique::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 +struct type_list_contains; + + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other>: std::disjunction...> {}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr auto type_list_contains_v = type_list_contains::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +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 +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 +struct value_list_element> + : value_list_element> +{}; + + +/** + * @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 +struct value_list_element<0u, value_list> { + /*! @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 +inline constexpr auto value_list_element_v = value_list_element::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 +constexpr value_list operator+(value_list, value_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +[[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) +-> decltype(std::declval() == std::declval()) { return true; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>); +} + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type Potentially equality comparable type. */ -template> -struct is_equality_comparable: std::false_type {}; - - -/*! @copydoc is_equality_comparable */ -template -struct is_equality_comparable() == std::declval())>> - : std::true_type -{}; +template +struct is_equality_comparable: std::bool_constant(choice<2>)> {}; /** @@ -12610,15 +28355,96 @@ template inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; +/*! @brief Same as std::is_invocable, but with tuples. */ +template +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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** +* @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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr auto is_applicable_v = is_applicable::value; + + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +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 +struct is_applicable_r>: std::is_invocable_r {}; + + +/** + * @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 +inline constexpr auto is_applicable_r_v = is_applicable_r::value; + + +/** +* @brief Provides the member constant `value` to true if a given type is +* complete, false otherwise. +* @tparam Type Potential complete type. +*/ +template +struct is_complete: std::false_type {}; + + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + + +/** +* @brief Helper variable template. +* @tparam Type Potential complete type. +*/ +template +inline constexpr auto is_complete_v = is_complete::value; + + /** * @brief Provides the member constant `value` to true if a given type is empty * and the empty type optimization is enabled, false otherwise. * @tparam Type Potential empty type. */ template -struct is_eto_eligible - : ENTT_IS_EMPTY(Type) -{}; +struct is_empty: ENTT_IS_EMPTY(Type) {}; /** @@ -12626,7 +28452,36 @@ struct is_eto_eligible * @tparam Type Potential empty type. */ template -inline constexpr auto is_eto_eligible_v = is_eto_eligible::value; +inline constexpr auto is_empty_v = is_empty::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 +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::add_const_t; +}; + + +/** + * @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 +using constness_as_t = typename constness_as::type; /** @@ -12665,401 +28520,27 @@ using member_class_t = typename member_class::type; #endif -// #include "internal.hpp" -#ifndef ENTT_META_INTERNAL_HPP -#define ENTT_META_INTERNAL_HPP - - -#include -#include -#include -#include -#include -// #include "../core/attribute.h" - -// #include "../config/config.h" - -// #include "../core/fwd.hpp" - -// #include "../core/type_info.hpp" - -// #include "../core/type_traits.hpp" - -// #include "type_traits.hpp" - namespace entt { -class meta_any; -struct meta_handle; - - /** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. + * @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 +class basic_any { + enum class operation { COPY, MOVE, DTOR, COMP, ADDR, CADDR, REF, CREF, TYPE }; - -namespace internal { - - -class meta_storage { - using storage_type = std::aligned_storage_t; - using copy_fn_type = void(meta_storage &, const meta_storage &); - using steal_fn_type = void(meta_storage &, meta_storage &); - using destroy_fn_type = void(meta_storage &); - - template> - struct type_traits { - template - static void instance(meta_storage &buffer, Args &&... args) { - buffer.instance = new Type{std::forward(args)...}; - new (&buffer.storage) Type *{static_cast(buffer.instance)}; - } - - static void destroy(meta_storage &buffer) { - delete static_cast(buffer.instance); - } - - static void copy(meta_storage &to, const meta_storage &from) { - to.instance = new Type{*static_cast(from.instance)}; - new (&to.storage) Type *{static_cast(to.instance)}; - } - - static void steal(meta_storage &to, meta_storage &from) { - new (&to.storage) Type *{static_cast(from.instance)}; - to.instance = from.instance; - } - }; + using storage_type = std::aligned_storage_t; + using vtable_type = const void *(const operation, const basic_any &, const void *); template - struct type_traits>> { - template - static void instance(meta_storage &buffer, Args &&... args) { - buffer.instance = new (&buffer.storage) Type{std::forward(args)...}; - } - - static void destroy(meta_storage &buffer) { - static_cast(buffer.instance)->~Type(); - } - - static void copy(meta_storage &to, const meta_storage &from) { - to.instance = new (&to.storage) Type{*static_cast(from.instance)}; - } - - static void steal(meta_storage &to, meta_storage &from) { - to.instance = new (&to.storage) Type{std::move(*static_cast(from.instance))}; - destroy(from); - } - }; - -public: - /*! @brief Default constructor. */ - meta_storage() ENTT_NOEXCEPT - : storage{}, - instance{}, - destroy_fn{}, - copy_fn{}, - steal_fn{} - {} - - template - explicit meta_storage(std::in_place_type_t, [[maybe_unused]] Args &&... args) - : meta_storage{} - { - if constexpr(!std::is_void_v) { - type_traits::instance(*this, std::forward(args)...); - destroy_fn = &type_traits::destroy; - copy_fn = &type_traits::copy; - steal_fn = &type_traits::steal; - } - } + static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; template - meta_storage(std::reference_wrapper value) - : meta_storage{} - { - instance = &value.get(); - } - - template>, meta_storage>>> - meta_storage(Type &&value) - : meta_storage{std::in_place_type>>, std::forward(value)} - {} - - meta_storage(const meta_storage &other) - : meta_storage{} - { - (other.copy_fn ? other.copy_fn : [](auto &to, const auto &from) { to.instance = from.instance; })(*this, other); - destroy_fn = other.destroy_fn; - copy_fn = other.copy_fn; - steal_fn = other.steal_fn; - } - - meta_storage(meta_storage &&other) - : meta_storage{} - { - swap(*this, other); - } - - ~meta_storage() { - if(destroy_fn) { - destroy_fn(*this); - } - } - - meta_storage & operator=(meta_storage other) { - swap(other, *this); - return *this; - } - - [[nodiscard]] const void * data() const ENTT_NOEXCEPT { - return instance; - } - - [[nodiscard]] void * data() ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).data()); - } - - template - void emplace(Args &&... args) { - *this = meta_storage{std::in_place_type, std::forward(args)...}; - } - - [[nodiscard]] meta_storage ref() const ENTT_NOEXCEPT { - meta_storage other{}; - other.instance = instance; - return other; - } - - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return !(instance == nullptr); - } - - friend void swap(meta_storage &lhs, meta_storage &rhs) { - using std::swap; - - if(lhs.steal_fn && rhs.steal_fn) { - meta_storage buffer{}; - lhs.steal_fn(buffer, lhs); - rhs.steal_fn(lhs, rhs); - lhs.steal_fn(rhs, buffer); - } else if(lhs.steal_fn) { - lhs.steal_fn(rhs, lhs); - } else if(rhs.steal_fn) { - rhs.steal_fn(lhs, rhs); - } else { - swap(lhs.instance, rhs.instance); - } - - swap(lhs.destroy_fn, rhs.destroy_fn); - swap(lhs.copy_fn, rhs.copy_fn); - swap(lhs.steal_fn, rhs.steal_fn); - } - -private: - storage_type storage; - void *instance; - destroy_fn_type *destroy_fn; - copy_fn_type *copy_fn; - steal_fn_type *steal_fn; -}; - - -struct meta_type_node; - - -struct meta_prop_node { - meta_prop_node * next; - meta_any(* const key)(); - meta_any(* const value)(); -}; - - -struct meta_base_node { - meta_type_node * const parent; - meta_base_node * next; - meta_type_node *(* const type)() ENTT_NOEXCEPT; - const void *(* const cast)(const void *) ENTT_NOEXCEPT; -}; - - -struct meta_conv_node { - meta_type_node * const parent; - meta_conv_node * next; - meta_type_node *(* const type)() ENTT_NOEXCEPT; - meta_any(* const conv)(const void *); -}; - - -struct meta_ctor_node { - using size_type = std::size_t; - meta_type_node * const parent; - meta_ctor_node * next; - meta_prop_node * prop; - const size_type size; - meta_type_node *(* const arg)(size_type) ENTT_NOEXCEPT; - meta_any(* const invoke)(meta_any * const); -}; - - -struct meta_data_node { - id_type id; - meta_type_node * const parent; - meta_data_node * next; - meta_prop_node * prop; - const bool is_static; - meta_type_node *(* const type)() ENTT_NOEXCEPT; - bool(* const set)(meta_handle, meta_any); - meta_any(* const get)(meta_handle); -}; - - -struct meta_func_node { - using size_type = std::size_t; - id_type id; - meta_type_node * const parent; - meta_func_node * next; - meta_prop_node * prop; - const size_type size; - const bool is_const; - const bool is_static; - meta_type_node *(* const ret)() ENTT_NOEXCEPT; - meta_type_node *(* const arg)(size_type) ENTT_NOEXCEPT; - meta_any(* const invoke)(meta_handle, meta_any *); -}; - - -struct meta_type_node { - using size_type = std::size_t; - const id_type type_id; - id_type id; - meta_type_node * next; - meta_prop_node * prop; - const bool is_void; - const bool is_integral; - const bool is_floating_point; - const bool is_array; - const bool is_enum; - const bool is_union; - const bool is_class; - const bool is_pointer; - const bool is_function_pointer; - const bool is_member_object_pointer; - const bool is_member_function_pointer; - const bool is_pointer_like; - const bool is_sequence_container; - const bool is_associative_container; - const size_type rank; - size_type(* const extent)(size_type); - bool(* const compare)(const void *, const void *); - meta_type_node *(* const remove_pointer)() ENTT_NOEXCEPT; - meta_type_node *(* const remove_extent)() ENTT_NOEXCEPT; - meta_base_node *base{nullptr}; - meta_conv_node *conv{nullptr}; - meta_ctor_node *ctor{nullptr}; - meta_data_node *data{nullptr}; - meta_func_node *func{nullptr}; - void(* dtor)(void *){nullptr}; -}; - - -template -class meta_range { - struct range_iterator { - using difference_type = std::ptrdiff_t; - using value_type = Node; - using pointer = value_type *; - using reference = value_type &; - using iterator_category = std::forward_iterator_tag; - - range_iterator() ENTT_NOEXCEPT = default; - - range_iterator(Node *head) ENTT_NOEXCEPT - : node{head} - {} - - range_iterator & operator++() ENTT_NOEXCEPT { - return node = node->next, *this; - } - - range_iterator operator++(int) ENTT_NOEXCEPT { - range_iterator orig = *this; - return ++(*this), orig; - } - - [[nodiscard]] bool operator==(const range_iterator &other) const ENTT_NOEXCEPT { - return other.node == node; - } - - [[nodiscard]] bool operator!=(const range_iterator &other) const ENTT_NOEXCEPT { - return !(*this == other); - } - - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { - return node; - } - - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { - return *operator->(); - } - - private: - Node *node{nullptr}; - }; - -public: - using iterator = range_iterator; - - meta_range() ENTT_NOEXCEPT = default; - - meta_range(Node *head) - : node{head} - {} - - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { - return iterator{node}; - } - - [[nodiscard]] iterator end() const ENTT_NOEXCEPT { - return iterator{}; - } - -private: - Node *node{nullptr}; -}; - - -template -auto find_if(const Op &op, const meta_type_node *node) --> std::decay_t*Member)> { - std::decay_t*Member)> ret = nullptr; - - for(auto &&curr: meta_range{node->*Member}) { - if(op(&curr)) { - ret = &curr; - break; - } - } - - if(!ret) { - for(auto &&curr: meta_range{node->base}) { - if(ret = find_if(op, curr.type()); ret) { - break; - } - } - } - - return ret; -} - - -template -class ENTT_API meta_node { - static_assert(std::is_same_v>>, "Invalid type"); - [[nodiscard]] static bool compare(const void *lhs, const void *rhs) { if constexpr(!std::is_function_v && is_equality_comparable_v) { return *static_cast(lhs) == *static_cast(rhs); @@ -13068,59 +28549,413 @@ class ENTT_API meta_node { } } - template - [[nodiscard]] static auto extent(meta_type_node::size_type dim, std::index_sequence) { - meta_type_node::size_type ext{}; - ((ext = (dim == Index ? std::extent_v : ext)), ...); - return ext; + template + static Type & as(const void *to) { + return *const_cast(static_cast(to)); + } + + template + static const void * basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &from, [[maybe_unused]] const void *to) { + if constexpr(std::is_void_v) { + switch(op) { + case operation::COPY: + case operation::MOVE: + case operation::REF: + case operation::CREF: + as(to).vtable = from.vtable; + break; + default: + break; + } + } else if constexpr(std::is_lvalue_reference_v) { + using base_type = std::decay_t; + + switch(op) { + case operation::COPY: + if constexpr(std::is_copy_constructible_v) { + as(to) = *static_cast(from.instance); + } + break; + case operation::MOVE: + as(to).instance = from.instance; + as(to).vtable = from.vtable; + [[fallthrough]]; + case operation::DTOR: + break; + case operation::COMP: + return compare(from.instance, to) ? to : nullptr; + case operation::ADDR: + return std::is_const_v> ? nullptr : from.instance; + case operation::CADDR: + return from.instance; + case operation::REF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::CREF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::TYPE: + as(to) = type_id(); + break; + } + } else if constexpr(in_situ) { + #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L + auto *instance = const_cast(std::launder(reinterpret_cast(&from.storage))); + #else + auto *instance = const_cast(reinterpret_cast(&from.storage)); + #endif + + switch(op) { + case operation::COPY: + if constexpr(std::is_copy_constructible_v) { + new (&as(to).storage) Type{std::as_const(*instance)}; + as(to).vtable = from.vtable; + } + break; + case operation::MOVE: + new (&as(to).storage) Type{std::move(*instance)}; + as(to).vtable = from.vtable; + break; + case operation::DTOR: + instance->~Type(); + break; + case operation::COMP: + return compare(instance, to) ? to : nullptr; + case operation::ADDR: + case operation::CADDR: + return instance; + case operation::REF: + as(to).instance = instance; + as(to).vtable = basic_vtable; + break; + case operation::CREF: + as(to).instance = instance; + as(to).vtable = basic_vtable; + break; + case operation::TYPE: + as(to) = type_id(); + break; + } + } else { + switch(op) { + case operation::COPY: + if constexpr(std::is_copy_constructible_v) { + as(to).instance = new Type{*static_cast(from.instance)}; + as(to).vtable = from.vtable; + } + break; + case operation::MOVE: + as(to).instance = std::exchange(as(&from).instance, nullptr); + as(to).vtable = from.vtable; + break; + case operation::DTOR: + if constexpr(std::is_array_v) { + delete[] static_cast(from.instance); + } else { + delete static_cast(from.instance); + } + break; + case operation::COMP: + return compare(from.instance, to) ? to : nullptr; + case operation::ADDR: + case operation::CADDR: + return from.instance; + case operation::REF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::CREF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::TYPE: + as(to) = type_id(); + break; + } + } + + return nullptr; + } + + template + void initialize([[maybe_unused]] Args &&... args) { + if constexpr(!std::is_void_v) { + if constexpr(std::is_lvalue_reference_v) { + static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); + instance = (std::addressof(args), ...); + } else if constexpr(in_situ) { + if constexpr(std::is_aggregate_v) { + new (&storage) Type{std::forward(args)...}; + } else { + new (&storage) Type(std::forward(args)...); + } + } else { + if constexpr(std::is_aggregate_v) { + instance = new Type{std::forward(args)...}; + } else { + instance = new Type(std::forward(args)...); + } + } + } } public: - [[nodiscard]] static meta_type_node * resolve() ENTT_NOEXCEPT { - static meta_type_node node{ - type_info::id(), - {}, - nullptr, - nullptr, - std::is_void_v, - std::is_integral_v, - std::is_floating_point_v, - std::is_array_v, - std::is_enum_v, - std::is_union_v, - std::is_class_v, - std::is_pointer_v, - std::is_pointer_v && std::is_function_v>, - std::is_member_object_pointer_v, - std::is_member_function_pointer_v, - is_meta_pointer_like_v, - has_meta_sequence_container_traits_v, - has_meta_associative_container_traits_v, - std::rank_v, - [](meta_type_node::size_type dim) { - return extent(dim, std::make_index_sequence>{}); - }, - &compare, // workaround for an issue with VS2017 - &meta_node>>::resolve, - &meta_node>>::resolve - }; + /*! @brief Default constructor. */ + basic_any() ENTT_NOEXCEPT + : basic_any{std::in_place_type} + {} - return &node; + /** + * @brief Constructs an any by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&... args) + : instance{}, + vtable{&basic_vtable} + { + initialize(std::forward(args)...); } + + /** + * @brief Constructs an any that holds an unmanaged object. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template + basic_any(std::reference_wrapper value) ENTT_NOEXCEPT + : basic_any{std::in_place_type, value.get()} + {} + + /** + * @brief Constructs an any from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, basic_any>>> + basic_any(Type &&value) + : basic_any{std::in_place_type>, std::forward(value)} + {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + basic_any(const basic_any &other) + : basic_any{std::in_place_type} + { + other.vtable(operation::COPY, other, this); + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_any(basic_any &&other) ENTT_NOEXCEPT + : basic_any{std::in_place_type} + { + other.vtable(operation::MOVE, other, this); + } + + /*! @brief Frees the internal storage, whatever it means. */ + ~basic_any() { + vtable(operation::DTOR, *this, nullptr); + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ + basic_any & operator=(const basic_any &other) { + vtable(operation::DTOR, *this, nullptr); + 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) { + vtable(operation::DTOR, *this, nullptr); + other.vtable(operation::MOVE, other, this); + 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 + basic_any & operator=(std::reference_wrapper value) ENTT_NOEXCEPT { + emplace(value.get()); + 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 + std::enable_if_t, basic_any>, basic_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns the type of the contained object. + * @return The type of the contained object, if any. + */ + [[nodiscard]] type_info type() const ENTT_NOEXCEPT { + type_info info{}; + vtable(operation::TYPE, *this, &info); + return info; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void * data() const ENTT_NOEXCEPT { + return vtable(operation::CADDR, *this, nullptr); + } + + /*! @copydoc data */ + [[nodiscard]] void * data() ENTT_NOEXCEPT { + return const_cast(vtable(operation::ADDR, *this, nullptr)); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + void emplace(Args &&... args) { + std::exchange(vtable, &basic_vtable)(operation::DTOR, *this, nullptr); + initialize(std::forward(args)...); + } + + /*! @brief Destroys contained object */ + void reset() { + std::exchange(vtable, &basic_vtable)(operation::DTOR, *this, nullptr); + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(vtable(operation::CADDR, *this, nullptr) == nullptr); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ + bool operator==(const basic_any &other) const ENTT_NOEXCEPT { + return type() == other.type() && (vtable(operation::COMP, *this, other.data()) == other.data()); + } + + /** + * @brief Aliasing constructor. + * @return An any that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT { + basic_any ref{}; + vtable(operation::REF, *this, &ref); + return ref; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT { + basic_any ref{}; + vtable(operation::CREF, *this, &ref); + return ref; + } + +private: + union { const void *instance; storage_type storage; }; + vtable_type *vtable; }; -template -struct meta_info: meta_node>...> {}; - - +/** + * @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 +[[nodiscard]] inline bool operator!=(const basic_any &lhs, const basic_any &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); } /** - * Internal details not to be documented. - * @endcond + * @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 +Type any_cast(const basic_any &data) ENTT_NOEXCEPT { + const auto * const instance = any_cast>(&data); + ENTT_ASSERT(instance); + return static_cast(*instance); +} + + +/*! @copydoc any_cast */ +template +Type any_cast(basic_any &data) ENTT_NOEXCEPT { + // forces const on non-reference types to make them work also with wrappers for const references + auto * const instance = any_cast>(&data); + ENTT_ASSERT(instance); + return static_cast(*instance); +} + + +/*! @copydoc any_cast */ +template +Type any_cast(basic_any &&data) ENTT_NOEXCEPT { + // forces const on non-reference types to make them work also with wrappers for const references + auto * const instance = any_cast>(&data); + ENTT_ASSERT(instance); + return static_cast(std::move(*instance)); +} + + +/*! @copydoc any_cast */ +template +const Type * any_cast(const basic_any *data) ENTT_NOEXCEPT { + return (data->type() == type_id() ? static_cast(data->data()) : nullptr); +} + + +/*! @copydoc any_cast */ +template +Type * any_cast(basic_any *data) ENTT_NOEXCEPT { + // last attempt to make wrappers for const references return their values + return (data->type() == type_id() ? static_cast(static_cast, Type> *>(data)->data()) : nullptr); +} } @@ -13128,20 +28963,6 @@ struct meta_info: meta_node>...> #endif -// #include "meta.hpp" -#ifndef ENTT_META_META_HPP -#define ENTT_META_META_HPP - - -#include -#include -#include -#include -#include -#include -#include -// #include "../config/config.h" - // #include "../core/fwd.hpp" // #include "../core/utility.hpp" @@ -13208,7 +29029,8 @@ struct overloaded: Func... { * @tparam Func Types of function objects. */ template -overloaded(Func...) -> overloaded; +overloaded(Func...) +-> overloaded; /** @@ -13250,18 +29072,62 @@ private: } +#endif + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #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 +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 +struct adl_meta_pointer_like { + /** + * @brief Uses the default ADL based lookup method to resolve the call. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ + static decltype(auto) dereference(const Type &value) { + return dereference_meta_pointer_like(value); + } +}; + + +} + + #endif // #include "ctx.hpp" - -// #include "internal.hpp" - -// #include "range.hpp" -#ifndef ENTT_META_RANGE_HPP -#define ENTT_META_RANGE_HPP +#ifndef ENTT_META_CTX_HPP +#define ENTT_META_CTX_HPP -// #include "internal.hpp" +// #include "../core/attribute.h" + +// #include "../config/config.h" @@ -13269,10 +29135,359 @@ namespace entt { /** - * @brief Iterable range to use to iterate all types of meta objects. - * @tparam Type Type of meta objects iterated. + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. */ + + +namespace internal { + + +struct meta_type_node; + + +struct ENTT_API meta_context { + // we could use the lines below but VS2017 returns with an ICE if combined with ENTT_API despite the code being valid C++ + // inline static meta_type_node *local = nullptr; + // inline static meta_type_node **global = &local; + + [[nodiscard]] static meta_type_node * & local() ENTT_NOEXCEPT { + static meta_type_node *chain = nullptr; + return chain; + } + + [[nodiscard]] static meta_type_node ** & global() ENTT_NOEXCEPT { + static meta_type_node **chain = &local(); + return chain; + } +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/*! @brief Opaque container for a meta context. */ +struct meta_ctx { + /** + * @brief Binds the meta system to a given context. + * @param other A valid context to which to bind. + */ + static void bind(meta_ctx other) ENTT_NOEXCEPT { + internal::meta_context::global() = other.ctx; + } + +private: + internal::meta_type_node **ctx{&internal::meta_context::local()}; +}; + + +} + + +#endif + +// #include "node.hpp" +#ifndef ENTT_META_NODE_HPP +#define ENTT_META_NODE_HPP + + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "type_traits.hpp" + + + +namespace entt { + + +class meta_any; +class meta_type; +struct meta_handle; + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct meta_type_node; + + +struct meta_prop_node { + meta_prop_node * next; + const meta_any &id; + meta_any &value; +}; + + +struct meta_base_node { + meta_type_node * const parent; + meta_base_node * next; + meta_type_node *(* const type)() ENTT_NOEXCEPT; + const void *(* const cast)(const void *) ENTT_NOEXCEPT; +}; + + +struct meta_conv_node { + meta_type_node * const parent; + meta_conv_node * next; + meta_type_node *(* const type)() ENTT_NOEXCEPT; + meta_any(* const conv)(const void *); +}; + + +struct meta_ctor_node { + using size_type = std::size_t; + meta_type_node * const parent; + meta_ctor_node * next; + meta_prop_node * prop; + const size_type arity; + meta_type(* const arg)(const size_type) ENTT_NOEXCEPT; + meta_any(* const invoke)(meta_any * const); +}; + + +struct meta_data_node { + id_type id; + meta_type_node * const parent; + meta_data_node * next; + meta_prop_node * prop; + const bool is_const; + const bool is_static; + meta_type_node *(* const type)() ENTT_NOEXCEPT; + bool(* const set)(meta_handle, meta_any); + meta_any(* const get)(meta_handle); +}; + + +struct meta_func_node { + using size_type = std::size_t; + id_type id; + meta_type_node * const parent; + meta_func_node * next; + meta_prop_node * prop; + const size_type arity; + const bool is_const; + const bool is_static; + meta_type_node *(* const ret)() ENTT_NOEXCEPT; + meta_type(* const arg)(const size_type) ENTT_NOEXCEPT; + meta_any(* const invoke)(meta_handle, meta_any *); +}; + + +struct meta_template_info { + using size_type = std::size_t; + const bool is_template_specialization; + const size_type arity; + meta_type_node *(* const type)() ENTT_NOEXCEPT; + meta_type_node *(* const arg)(const size_type) ENTT_NOEXCEPT; +}; + + +struct meta_type_node { + using size_type = std::size_t; + const type_info info; + id_type id; + meta_type_node * next; + meta_prop_node * prop; + const size_type size_of; + const bool is_void; + const bool is_integral; + const bool is_floating_point; + const bool is_array; + const bool is_enum; + const bool is_union; + const bool is_class; + const bool is_pointer; + const bool is_function_pointer; + const bool is_member_object_pointer; + const bool is_member_function_pointer; + const bool is_pointer_like; + const bool is_sequence_container; + const bool is_associative_container; + const meta_template_info template_info; + const size_type rank; + size_type(* const extent)(const size_type) ENTT_NOEXCEPT ; + meta_type_node *(* const remove_pointer)() ENTT_NOEXCEPT; + meta_type_node *(* const remove_extent)() ENTT_NOEXCEPT; + meta_ctor_node * const def_ctor; + meta_ctor_node *ctor{nullptr}; + meta_base_node *base{nullptr}; + meta_conv_node *conv{nullptr}; + meta_data_node *data{nullptr}; + meta_func_node *func{nullptr}; + void(* dtor)(void *){nullptr}; +}; + + +template +auto meta_visit(const Op &op, const Node *node) +-> std::decay_t*Member)> { + for(auto *curr = node->*Member; curr; curr = curr->next) { + if(op(curr)) { + return curr; + } + } + + if constexpr(std::is_same_v) { + for(auto *curr = node->base; curr; curr = curr->next) { + if(auto *ret = meta_visit(op, curr->type()); ret) { + return ret; + } + } + } + + return nullptr; +} + + +template +meta_type_node * meta_arg_node(type_list, const std::size_t index) ENTT_NOEXCEPT; + + template +class ENTT_API meta_node { + static_assert(std::is_same_v>>, "Invalid type"); + + template + [[nodiscard]] static auto extent(const meta_type_node::size_type dim, std::index_sequence) ENTT_NOEXCEPT { + meta_type_node::size_type ext{}; + ((ext = (dim == Index ? std::extent_v : ext)), ...); + return ext; + } + + [[nodiscard]] static meta_ctor_node * meta_default_constructor([[maybe_unused]] meta_type_node *type) ENTT_NOEXCEPT { + if constexpr(std::is_default_constructible_v) { + static meta_ctor_node node{ + type, + nullptr, + nullptr, + 0u, + nullptr, + [](meta_any * const) { return meta_any{std::in_place_type}; } + }; + + return &node; + } else { + return nullptr; + } + } + + [[nodiscard]] static meta_template_info meta_template_descriptor() ENTT_NOEXCEPT { + if constexpr(is_complete_v>) { + return { + true, + meta_template_traits::args_type::size, + &meta_node::class_type>::resolve, + [](const std::size_t index) ENTT_NOEXCEPT { + return meta_arg_node(typename meta_template_traits::args_type{}, index); + } + }; + } else { + return { false, 0u, nullptr, nullptr }; + } + } + +public: + [[nodiscard]] static meta_type_node * resolve() ENTT_NOEXCEPT { + static meta_type_node node{ + type_id(), + {}, + nullptr, + nullptr, + size_of_v, + std::is_void_v, + std::is_integral_v, + std::is_floating_point_v, + std::is_array_v, + std::is_enum_v, + std::is_union_v, + std::is_class_v, + std::is_pointer_v, + std::is_pointer_v && std::is_function_v>, + std::is_member_object_pointer_v, + std::is_member_function_pointer_v, + is_meta_pointer_like_v, + is_complete_v>, + is_complete_v>, + meta_template_descriptor(), + std::rank_v, + [](meta_type_node::size_type dim) ENTT_NOEXCEPT { return extent(dim, std::make_index_sequence>{}); }, + &meta_node>>>::resolve, + &meta_node>>>::resolve, + meta_default_constructor(&node), + meta_default_constructor(&node) + }; + + return &node; + } +}; + + +template +struct meta_info: meta_node>> {}; + + +template +meta_type_node * meta_arg_node(type_list, const std::size_t index) ENTT_NOEXCEPT { + meta_type_node *args[sizeof...(Args) + 1u]{nullptr, internal::meta_info::resolve()...}; + return args[index + 1u]; +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +} + + +#endif + +// #include "range.hpp" +#ifndef ENTT_META_RANGE_HPP +#define ENTT_META_RANGE_HPP + + +#include +#include + + +namespace entt { + + +/** + * @brief Iterable range to use to iterate all types of meta objects. + * @tparam Type Type of meta objects returned. + * @tparam Node Type of meta nodes iterated. + */ +template class meta_range { struct range_iterator { using difference_type = std::ptrdiff_t; @@ -13280,7 +29495,7 @@ class meta_range { using pointer = void; using reference = value_type; using iterator_category = std::input_iterator_tag; - using node_type = typename Type::node_type; + using node_type = Node; range_iterator() ENTT_NOEXCEPT = default; @@ -13289,7 +29504,7 @@ class meta_range { {} range_iterator & operator++() ENTT_NOEXCEPT { - return ++it, *this; + return (it = it->next), *this; } range_iterator operator++(int) ENTT_NOEXCEPT { @@ -13298,7 +29513,7 @@ class meta_range { } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { - return it.operator->(); + return it; } [[nodiscard]] bool operator==(const range_iterator &other) const ENTT_NOEXCEPT { @@ -13310,12 +29525,12 @@ class meta_range { } private: - typename internal::meta_range::iterator it{}; + node_type *it{}; }; public: /*! @brief Node type. */ - using node_type = typename Type::node_type; + using node_type = Node; /*! @brief Input iterator type. */ using iterator = range_iterator; @@ -13364,8 +29579,8 @@ private: namespace entt { -class meta_type; class meta_any; +class meta_type; /*! @brief Proxy object for sequence containers. */ @@ -13382,17 +29597,15 @@ public: using iterator = meta_iterator; /*! @brief Default constructor. */ - meta_sequence_container() ENTT_NOEXCEPT - : instance{nullptr} - {} + meta_sequence_container() ENTT_NOEXCEPT = default; /** * @brief Construct a proxy object for sequence containers. * @tparam Type Type of container to wrap. - * @param container The container to wrap. + * @param instance The container to wrap. */ template - meta_sequence_container(Type *container) ENTT_NOEXCEPT + meta_sequence_container(std::in_place_type_t, any instance) ENTT_NOEXCEPT : value_type_fn{&meta_sequence_container_proxy::value_type}, size_fn{&meta_sequence_container_proxy::size}, resize_fn{&meta_sequence_container_proxy::resize}, @@ -13402,12 +29615,12 @@ public: insert_fn{&meta_sequence_container_proxy::insert}, erase_fn{&meta_sequence_container_proxy::erase}, get_fn{&meta_sequence_container_proxy::get}, - instance{container} + storage{std::move(instance)} {} [[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; [[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; - inline bool resize(size_type) const; + inline bool resize(size_type); inline bool clear(); [[nodiscard]] inline iterator begin(); [[nodiscard]] inline iterator end(); @@ -13417,16 +29630,16 @@ public: [[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; private: - meta_type(* value_type_fn)() ENTT_NOEXCEPT; - size_type(* size_fn)(const void *) ENTT_NOEXCEPT; - bool(* resize_fn)(void *, size_type); - bool(* clear_fn)(void *); - iterator(* begin_fn)(void *); - iterator(* end_fn)(void *); - std::pair(* insert_fn)(void *, iterator, meta_any); - std::pair(* erase_fn)(void *, iterator); - meta_any(* get_fn)(void *, size_type); - void *instance; + meta_type(* value_type_fn)() ENTT_NOEXCEPT = nullptr; + size_type(* size_fn)(const any &) ENTT_NOEXCEPT = nullptr; + bool(* resize_fn)(any &, size_type) = nullptr; + bool(* clear_fn)(any &) = nullptr; + iterator(* begin_fn)(any &) = nullptr; + iterator(* end_fn)(any &) = nullptr; + std::pair(* insert_fn)(any &, iterator, meta_any &) = nullptr; + std::pair(* erase_fn)(any &, iterator) = nullptr; + meta_any(* get_fn)(any &, size_type) = nullptr; + any storage{}; }; @@ -13444,17 +29657,15 @@ public: using iterator = meta_iterator; /*! @brief Default constructor. */ - meta_associative_container() ENTT_NOEXCEPT - : instance{nullptr} - {} + meta_associative_container() ENTT_NOEXCEPT = default; /** * @brief Construct a proxy object for associative containers. * @tparam Type Type of container to wrap. - * @param container The container to wrap. + * @param instance The container to wrap. */ template - meta_associative_container(Type *container) ENTT_NOEXCEPT + meta_associative_container(std::in_place_type_t, any instance) ENTT_NOEXCEPT : key_only_container{is_key_only_meta_associative_container_v}, key_type_fn{&meta_associative_container_proxy::key_type}, mapped_type_fn{&meta_associative_container_proxy::mapped_type}, @@ -13466,7 +29677,7 @@ public: insert_fn{&meta_associative_container_proxy::insert}, erase_fn{&meta_associative_container_proxy::erase}, find_fn{&meta_associative_container_proxy::find}, - instance{container} + storage{std::move(instance)} {} [[nodiscard]] inline bool key_only() const ENTT_NOEXCEPT; @@ -13483,58 +29694,74 @@ public: [[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; private: - bool key_only_container; - meta_type(* key_type_fn)() ENTT_NOEXCEPT; - meta_type(* mapped_type_fn)() ENTT_NOEXCEPT; - meta_type(* value_type_fn)() ENTT_NOEXCEPT; - size_type(* size_fn)(const void *) ENTT_NOEXCEPT; - bool(* clear_fn)(void *); - iterator(* begin_fn)(void *); - iterator(* end_fn)(void *); - bool(* insert_fn)(void *, meta_any, meta_any); - bool(* erase_fn)(void *, meta_any); - iterator(* find_fn)(void *, meta_any); - void *instance; + bool key_only_container{}; + meta_type(* key_type_fn)() ENTT_NOEXCEPT = nullptr; + meta_type(* mapped_type_fn)() ENTT_NOEXCEPT = nullptr; + meta_type(* value_type_fn)() ENTT_NOEXCEPT = nullptr; + size_type(* size_fn)(const any &) ENTT_NOEXCEPT = nullptr; + bool(* clear_fn)(any &) = nullptr; + iterator(* begin_fn)(any &) = nullptr; + iterator(* end_fn)(any &) = 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. - * - * This class uses a technique called small buffer optimization (SBO) to get rid - * of memory allocations if possible. This should improve overall performance. - */ +/*! @brief Opaque wrapper for values of any type. */ class meta_any { - using dereference_operator_type = meta_any(meta_any &); + enum class operation { DTOR, REF, CREF, DEREF, CDEREF, SEQ, CSEQ, ASSOC, CASSOC }; + + using vtable_type = void(const operation, const any &, void *); template - [[nodiscard]] static meta_any dereference_operator(meta_any &any) { - if constexpr(is_meta_pointer_like_v) { - if constexpr(std::is_const_v())>>) { - return *any.cast(); - } else { - return std::ref(*any.cast()); + static void basic_vtable(const operation op, [[maybe_unused]] const any &from, [[maybe_unused]] void *to) { + if constexpr(!std::is_void_v) { + switch(op) { + case operation::DTOR: + if constexpr(!std::is_lvalue_reference_v) { + if(auto *curr = static_cast(to); curr->dtor) { + curr->dtor(const_cast(from).data()); + } + } + break; + case operation::REF: + case operation::CREF: + *static_cast(to) = (op == operation::REF ? meta_any{std::ref(any_cast(const_cast(from)))} : meta_any{std::cref(any_cast &>(from))}); + break; + case operation::DEREF: + case operation::CDEREF: + if constexpr(is_meta_pointer_like_v>) { + using element_type = std::remove_const_t>::element_type>; + + if constexpr(std::is_function_v) { + *static_cast(to) = any_cast>(from); + } else if constexpr(!std::is_same_v) { + using adl_meta_pointer_like_type = adl_meta_pointer_like>; + + if constexpr(std::is_lvalue_reference_v &>()))>) { + auto &&obj = adl_meta_pointer_like_type::dereference(any_cast &>(from)); + *static_cast(to) = (op == operation::DEREF ? meta_any{std::ref(obj)} : meta_any{std::cref(obj)}); + } else { + *static_cast(to) = adl_meta_pointer_like_type::dereference(any_cast &>(from)); + } + } + } + break; + case operation::SEQ: + case operation::CSEQ: + if constexpr(is_complete_v>>) { + *static_cast(to) = { std::in_place_type>, (op == operation::SEQ ? const_cast(from).as_ref() : from.as_ref()) }; + } + break; + case operation::ASSOC: + case operation::CASSOC: + if constexpr(is_complete_v>>) { + *static_cast(to) = { std::in_place_type>, (op == operation::ASSOC ? const_cast(from).as_ref() : from.as_ref()) }; + } + break; } - } else { - return {}; - } - } - - template - [[nodiscard]] static meta_sequence_container meta_sequence_container_factory([[maybe_unused]] void *container) ENTT_NOEXCEPT { - if constexpr(has_meta_sequence_container_traits_v) { - return static_cast(container); - } else { - return {}; - } - } - - template - [[nodiscard]] static meta_associative_container meta_associative_container_factory([[maybe_unused]] void *container) ENTT_NOEXCEPT { - if constexpr(has_meta_associative_container_traits_v) { - return static_cast(container); - } else { - return {}; } } @@ -13543,9 +29770,7 @@ public: meta_any() ENTT_NOEXCEPT : storage{}, node{}, - deref{nullptr}, - seq_factory{nullptr}, - assoc_factory{nullptr} + vtable{&basic_vtable} {} /** @@ -13555,12 +29780,10 @@ public: * @param args Parameters to use to construct the instance. */ template - explicit meta_any(std::in_place_type_t, [[maybe_unused]] Args &&... args) - : storage(std::in_place_type, std::forward(args)...), + explicit meta_any(std::in_place_type_t, Args &&... args) + : storage{std::in_place_type, std::forward(args)...}, node{internal::meta_info::resolve()}, - deref{&dereference_operator}, - seq_factory{&meta_sequence_container_factory}, - assoc_factory{&meta_associative_container_factory} + vtable{&basic_vtable} {} /** @@ -13570,11 +29793,7 @@ public: */ template meta_any(std::reference_wrapper value) - : storage{value}, - node{internal::meta_info::resolve()}, - deref{&dereference_operator}, - seq_factory{&meta_sequence_container_factory}, - assoc_factory{&meta_associative_container_factory} + : meta_any{std::in_place_type, value.get()} {} /** @@ -13582,9 +29801,9 @@ public: * @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>, meta_any>>> + template, meta_any>>> meta_any(Type &&value) - : meta_any{std::in_place_type>>, std::forward(value)} + : meta_any{std::in_place_type>, std::forward(value)} {} /** @@ -13597,32 +29816,69 @@ public: * @brief Move constructor. * @param other The instance to move from. */ - meta_any(meta_any &&other) - : meta_any{} - { - swap(*this, other); - } + 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)} + {} /*! @brief Frees the internal storage, whatever it means. */ ~meta_any() { - if(node && node->dtor) { - node->dtor(storage.data()); - } + vtable(operation::DTOR, storage, node); } /** - * @brief Assignment operator. - * @param other The instance to assign from. + * @brief Copy assignment operator. + * @param other The instance to copy from. * @return This meta any object. */ - meta_any & operator=(meta_any other) { - swap(other, *this); + meta_any & operator=(const meta_any &other) { + std::exchange(vtable, other.vtable)(operation::DTOR, storage, node); + storage = other.storage; + node = other.node; return *this; } /** - * @brief Returns the meta type of the underlying object. - * @return The meta type of the underlying object, if any. + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This meta any object. + */ + meta_any & operator=(meta_any &&other) { + std::exchange(vtable, std::exchange(other.vtable, &basic_vtable))(operation::DTOR, storage, node); + 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 + meta_any & operator=(std::reference_wrapper value) { + emplace(value.get()); + 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 + std::enable_if_t, meta_any>, meta_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns the type of the underlying object. + * @return The type of the underlying object, if any. */ [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; @@ -13639,6 +29895,47 @@ public: 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 meta any containing the returned value, if any. + */ + template + meta_any invoke(const id_type id, Args &&... args) const; + + /*! @copydoc invoke */ + template + meta_any invoke(const id_type id, Args &&... args); + + /** + * @brief Sets the value of a given variable. + * + * The type of the value must be 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 + bool set(const id_type id, Type &&value); + + /** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @return A meta any 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. @@ -13647,9 +29944,9 @@ public: template [[nodiscard]] const Type * try_cast() const { if(node) { - if(const auto type_id = internal::meta_info::resolve()->type_id; node->type_id == type_id) { - return static_cast(storage.data()); - } else if(const auto *base = internal::find_if<&internal::meta_type_node::base>([type_id](const auto *curr) { return curr->type()->type_id == type_id; }, node); base) { + if(const auto info = type_id(); node->info == info) { + return any_cast(&storage); + } else if(const auto *base = internal::meta_visit<&internal::meta_type_node::base>([info](const auto *curr) { return curr->type()->info == info; }, node); base) { return static_cast(base->cast(storage.data())); } } @@ -13660,7 +29957,15 @@ public: /*! @copydoc try_cast */ template [[nodiscard]] Type * try_cast() { - return const_cast(std::as_const(*this).try_cast()); + if(node) { + if(const auto info = type_id(); node->info == info) { + return any_cast(&storage); + } else if(const auto *base = internal::meta_visit<&internal::meta_type_node::base>([info](const auto *curr) { return curr->type()->info == info; }, node); base) { + return static_cast(const_cast *>(base->cast(static_cast &>(storage).data()))); + } + } + + return nullptr; } /** @@ -13669,39 +29974,39 @@ public: * The type of the instance must be such that the cast is possible. * * @warning - * Attempting to perform a cast that isn't viable results in undefined - * behavior.
- * An assertion will abort the execution at runtime in debug mode in case - * the cast is not feasible. + * Attempting to perform an invalid cast results in undefined behavior. * * @tparam Type Type to which to cast the instance. * @return A reference to the contained instance. */ template - [[nodiscard]] const Type & cast() const { - auto * const actual = try_cast(); + [[nodiscard]] Type cast() const { + auto * const actual = try_cast>(); ENTT_ASSERT(actual); - return *actual; + return static_cast(*actual); } /*! @copydoc cast */ template - [[nodiscard]] Type & cast() { - return const_cast(std::as_const(*this).cast()); + [[nodiscard]] Type cast() { + // forces const on non-reference types to make them work also with wrappers for const references + auto * const instance = try_cast>(); + ENTT_ASSERT(instance); + return static_cast(*instance); } /** - * @brief Tries to convert an instance to a given type and returns it. - * @tparam Type Type to which to convert the instance. - * @return A valid meta any object if the conversion is possible, an invalid - * one otherwise. + * @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 - [[nodiscard]] meta_any convert() const { - if(node) { - if(const auto type_id = internal::meta_info::resolve()->type_id; node->type_id == type_id) { - return *this; - } else if(const auto * const conv = internal::find_if<&internal::meta_type_node::conv>([type_id](const auto *curr) { return curr->type()->type_id == type_id; }, node); conv) { + [[nodiscard]] meta_any allow_cast() const { + if(try_cast>() != nullptr) { + return as_ref(); + } else if(node) { + if(const auto * const conv = internal::meta_visit<&internal::meta_type_node::conv>([info = type_id()](const auto *curr) { return curr->type()->info == info; }, node); conv) { return conv->conv(storage.data()); } } @@ -13710,22 +30015,23 @@ public: } /** - * @brief Tries to convert an instance to a given type. - * @tparam Type Type to which to convert the instance. - * @return True if the conversion is possible, false otherwise. + * @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 - bool convert() { - bool valid = (node && node->type_id == internal::meta_info::resolve()->type_id); - - if(!valid) { - if(auto any = std::as_const(*this).convert(); any) { - swap(any, *this); - valid = true; + bool allow_cast() { + // forces const on non-reference types to make them work also with wrappers for const references + if(try_cast>() != nullptr) { + return true; + } else if(node) { + if(const auto * const conv = internal::meta_visit<&internal::meta_type_node::conv>([info = type_id()](const auto *curr) { return curr->type()->info == info; }, node); conv) { + *this = conv->conv(std::as_const(storage).data()); + return true; } } - return valid; + return false; } /** @@ -13736,21 +30042,16 @@ public: */ template void emplace(Args &&... args) { - *this = meta_any{std::in_place_type, std::forward(args)...}; + std::exchange(vtable, &basic_vtable)(operation::DTOR, storage, node); + storage.emplace(std::forward(args)...); + node = internal::meta_info::resolve(); } - /** - * @brief Aliasing constructor. - * @return A meta any that shares a reference to an unmanaged object. - */ - [[nodiscard]] meta_any ref() const ENTT_NOEXCEPT { - meta_any other{}; - other.node = node; - other.storage = storage.ref(); - other.deref = deref; - other.seq_factory = seq_factory; - other.assoc_factory = assoc_factory; - return other; + /*! @brief Destroys contained object */ + void reset() { + std::exchange(vtable, &basic_vtable)(operation::DTOR, storage, node); + storage.reset(); + node = nullptr; } /** @@ -13758,7 +30059,16 @@ public: * @return A sequence container proxy for the underlying object. */ [[nodiscard]] meta_sequence_container as_sequence_container() ENTT_NOEXCEPT { - return seq_factory(storage.data()); + meta_sequence_container proxy; + vtable(operation::SEQ, storage, &proxy); + return proxy; + } + + /*! @copydoc as_sequence_container */ + [[nodiscard]] meta_sequence_container as_sequence_container() const ENTT_NOEXCEPT { + meta_sequence_container proxy; + vtable(operation::CSEQ, storage, &proxy); + return proxy; } /** @@ -13766,7 +30076,16 @@ public: * @return An associative container proxy for the underlying object. */ [[nodiscard]] meta_associative_container as_associative_container() ENTT_NOEXCEPT { - return assoc_factory(storage.data()); + meta_associative_container proxy; + vtable(operation::ASSOC, storage, &proxy); + return proxy; + } + + /*! @copydoc as_associative_container */ + [[nodiscard]] meta_associative_container as_associative_container() const ENTT_NOEXCEPT { + meta_associative_container proxy; + vtable(operation::CASSOC, storage, &proxy); + return proxy; } /** @@ -13775,12 +30094,21 @@ public: * wrapped element is dereferenceable, an invalid meta any otherwise. */ [[nodiscard]] meta_any operator*() ENTT_NOEXCEPT { - return deref(*this); + meta_any ret{}; + vtable(operation::DEREF, storage, &ret); + return ret; + } + + /*! @copydoc operator* */ + [[nodiscard]] meta_any operator*() const ENTT_NOEXCEPT { + meta_any ret{}; + vtable(operation::CDEREF, storage, &ret); + return ret; } /** - * @brief Returns false if a wrapper is empty, true otherwise. - * @return False if the wrapper is empty, true otherwise. + * @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); @@ -13792,29 +30120,30 @@ public: * @return False if the two objects differ in their content, true otherwise. */ [[nodiscard]] bool operator==(const meta_any &other) const { - return (!node && !other.node) || (node && other.node && node->type_id == other.node->type_id && node->compare(storage.data(), other.storage.data())); + return (!node && !other.node) || (node && other.node && node->info == other.node->info && storage == other.storage); } /** - * @brief Swaps two meta any objects. - * @param lhs A valid meta any object. - * @param rhs A valid meta any object. + * @brief Aliasing constructor. + * @return A meta any that shares a reference to an unmanaged object. */ - friend void swap(meta_any &lhs, meta_any &rhs) { - using std::swap; - swap(lhs.storage, rhs.storage); - swap(lhs.node, rhs.node); - swap(lhs.deref, rhs.deref); - swap(lhs.seq_factory, rhs.seq_factory); - swap(lhs.assoc_factory, rhs.assoc_factory); + [[nodiscard]] meta_any as_ref() ENTT_NOEXCEPT { + meta_any ref{}; + vtable(operation::REF, storage, &ref); + return ref; + } + + /*! @copydoc as_ref */ + [[nodiscard]] meta_any as_ref() const ENTT_NOEXCEPT { + meta_any ref{}; + vtable(operation::CREF, storage, &ref); + return ref; } private: - internal::meta_storage storage; + any storage; internal::meta_type_node *node; - dereference_operator_type *deref; - meta_sequence_container(* seq_factory)(void *); - meta_associative_container(* assoc_factory)(void *); + vtable_type *vtable; }; @@ -13834,35 +30163,53 @@ private: * * A handle doesn't perform copies and isn't responsible for the contained * object. It doesn't prolong the lifetime of the pointed instance.
- * Handles are used to generate meta references to actual objects when needed. + * 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>, meta_handle>>> - meta_handle(Type &&value) ENTT_NOEXCEPT + template, meta_handle>>> + meta_handle(Type &value) ENTT_NOEXCEPT : meta_handle{} { - if constexpr(std::is_same_v>, meta_any>) { - any = value.ref(); + if constexpr(std::is_same_v, meta_any>) { + any = value.as_ref(); } else { - static_assert(std::is_lvalue_reference_v, "Lvalue reference required"); - any = std::ref(value); + any.emplace(value); } } /** - * @brief Dereference operator for accessing the contained opaque object. - * @return A meta any that shares a reference to an unmanaged object. + * @brief Returns false if a handle is invalid, true otherwise. + * @return False if the handle is invalid, true otherwise. */ - [[nodiscard]] meta_any operator*() const { - return any; + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(any); } /** @@ -13873,12 +30220,17 @@ struct meta_handle { return &any; } + /*! @copydoc operator-> */ + [[nodiscard]] const meta_any * operator->() const { + return &any; + } + private: meta_any any; }; -/*! @brief Opaque wrapper for meta properties of any type. */ +/*! @brief Opaque wrapper for properties of any type. */ struct meta_prop { /*! @brief Node type. */ using node_type = internal::meta_prop_node; @@ -13892,24 +30244,24 @@ struct meta_prop { {} /** - * @brief Returns the stored key. + * @brief Returns the stored key as a const reference. * @return A meta any containing the key stored with the property. */ [[nodiscard]] meta_any key() const { - return node->key(); + return node->id.as_ref(); } /** - * @brief Returns the stored value. + * @brief Returns the stored value by copy. * @return A meta any containing the value stored with the property. */ [[nodiscard]] meta_any value() const { - return node->value(); + return node->value; } /** - * @brief Returns true if a meta object is valid, false otherwise. - * @return True if the meta object is valid, false otherwise. + * @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); @@ -13920,86 +30272,7 @@ private: }; -/*! @brief Opaque wrapper for meta base classes. */ -struct meta_base { - /*! @brief Node type. */ - using node_type = internal::meta_base_node; - - /*! @copydoc meta_prop::meta_prop */ - meta_base(const node_type *curr = nullptr) ENTT_NOEXCEPT - : node{curr} - {} - - /** - * @brief Returns the meta type to which a meta object belongs. - * @return The meta type to which the meta object belongs. - */ - [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; - - /*! @copydoc meta_any::type */ - [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; - - /** - * @brief Casts an instance from a parent type to a base type. - * @param instance The instance to cast. - * @return An opaque pointer to the base type. - */ - [[nodiscard]] const void * cast(const void *instance) const ENTT_NOEXCEPT { - return node->cast(instance); - } - - /** - * @brief Returns true if a meta object is valid, false otherwise. - * @return True if the meta object is valid, false otherwise. - */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return !(node == nullptr); - } - -private: - const node_type *node; -}; - - -/*! @brief Opaque wrapper for meta conversion functions. */ -struct meta_conv { - /*! @brief Node type. */ - using node_type = internal::meta_conv_node; - - /*! @copydoc meta_prop::meta_prop */ - meta_conv(const node_type *curr = nullptr) ENTT_NOEXCEPT - : node{curr} - {} - - /*! @copydoc meta_base::parent */ - [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; - - /*! @copydoc meta_any::type */ - [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; - - /** - * @brief Converts an instance to the underlying type. - * @param instance The instance to convert. - * @return An opaque pointer to the instance to convert. - */ - [[nodiscard]] meta_any convert(const void *instance) const { - return node->conv(instance); - } - - /** - * @brief Returns true if a meta object is valid, false otherwise. - * @return True if the meta object is valid, false otherwise. - */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return !(node == nullptr); - } - -private: - const node_type *node; -}; - - -/*! @brief Opaque wrapper for meta constructors. */ +/*! @brief Opaque wrapper for constructors. */ struct meta_ctor { /*! @brief Node type. */ using node_type = internal::meta_ctor_node; @@ -14011,37 +30284,39 @@ struct meta_ctor { : node{curr} {} - /*! @copydoc meta_base::parent */ + /** + * @brief Returns the type to which an object belongs. + * @return The type to which the object belongs. + */ [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; /** - * @brief Returns the number of arguments accepted by a meta constructor. - * @return The number of arguments accepted by the meta constructor. + * @brief Returns the number of arguments accepted by a constructor. + * @return The number of arguments accepted by the constructor. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { - return node->size; + [[nodiscard]] size_type arity() const ENTT_NOEXCEPT { + return node->arity; } /** - * @brief Returns the meta type of the i-th argument of a meta constructor. - * @param index The index of the argument of which to return the meta type. - * @return The meta type of the i-th argument of a meta constructor, if any. + * @brief Returns the type of the i-th argument of a constructor. + * @param index Index of the argument of which to return the type. + * @return The type of the i-th argument of a constructor. */ [[nodiscard]] meta_type arg(size_type index) const ENTT_NOEXCEPT; /** * @brief Creates an instance of the underlying type, if possible. * - * To create a valid instance, 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. + * Parameters must be such that a cast or conversion to the required types + * is possible. Otherwise, an empty and thus invalid wrapper is returned. * * @param args Parameters to use to construct the instance. * @param sz Number of parameters to use to construct the instance. * @return A meta any containing the new instance, if any. */ - [[nodiscard]] meta_any invoke(meta_any * const args, const std::size_t sz) const { - return sz == size() ? node->invoke(args) : meta_any{}; + [[nodiscard]] meta_any invoke(meta_any * const args, const size_type sz) const { + return sz == arity() ? node->invoke(args) : meta_any{}; } /** @@ -14055,13 +30330,13 @@ struct meta_ctor { */ template [[nodiscard]] meta_any invoke([[maybe_unused]] Args &&... args) const { - std::array arguments{std::forward(args)...}; - return invoke(arguments.data(), sizeof...(Args)); + meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; + return invoke(arguments, sizeof...(Args)); } /** - * @brief Returns a range to use to visit all meta properties. - * @return An iterable range to use to visit all meta properties. + * @brief Returns a range to use to visit all properties. + * @return An iterable range to use to visit all properties. */ [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { return node->prop; @@ -14073,13 +30348,12 @@ struct meta_ctor { * @return The property associated with the given key, if any. */ [[nodiscard]] meta_prop prop(meta_any key) const { - internal::meta_range range{node->prop}; - return std::find_if(range.begin(), range.end(), [&key](const auto &curr) { return curr.key() == key; }).operator->(); + return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return curr->id == key; }, node); } /** - * @brief Returns true if a meta object is valid, false otherwise. - * @return True if the meta object is valid, false otherwise. + * @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); @@ -14090,7 +30364,7 @@ private: }; -/*! @brief Opaque wrapper for meta data. */ +/*! @brief Opaque wrapper for data members. */ struct meta_data { /*! @brief Node type. */ using node_type = internal::meta_data_node; @@ -14105,20 +30379,20 @@ struct meta_data { return node->id; } - /*! @copydoc meta_base::parent */ + /*! @copydoc meta_ctor::parent */ [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; /** - * @brief Indicates whether a meta data is constant or not. - * @return True if the meta data is constant, false otherwise. + * @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->set == nullptr); + return node->is_const; } /** - * @brief Indicates whether a meta data is static or not. - * @return True if the meta data is static, false otherwise. + * @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->is_static; @@ -14130,8 +30404,8 @@ struct meta_data { /** * @brief Sets the value of a given variable. * - * It must be possible to cast the instance to the parent type of the meta - * data. Otherwise, invoking the setter results in an undefined + * It must be possible to cast the instance to the parent type of the data + * member. Otherwise, invoking the setter results in an undefined * behavior.
* The type of the value must be such that a cast or conversion to the type * of the variable is possible. Otherwise, invoking the setter does nothing. @@ -14149,8 +30423,8 @@ struct meta_data { /** * @brief Gets the value of a given variable. * - * It must be possible to cast the instance to the parent type of the meta - * data. Otherwise, invoking the getter results in an undefined behavior. + * It must be possible to cast the instance to the parent type of the data + * member. Otherwise, invoking the getter results in an undefined behavior. * * @param instance An opaque instance of the underlying type. * @return A meta any containing the value of the underlying variable. @@ -14170,13 +30444,12 @@ struct meta_data { * @return The property associated with the given key, if any. */ [[nodiscard]] meta_prop prop(meta_any key) const { - internal::meta_range range{node->prop}; - return std::find_if(range.begin(), range.end(), [&key](const auto &curr) { return curr.key() == key; }).operator->(); + return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return curr->id == key; }, node); } /** - * @brief Returns true if a meta object is valid, false otherwise. - * @return True if the meta object is valid, false otherwise. + * @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); @@ -14187,7 +30460,7 @@ private: }; -/*! @brief Opaque wrapper for meta functions. */ +/*! @brief Opaque wrapper for member functions. */ struct meta_func { /*! @brief Node type. */ using node_type = internal::meta_func_node; @@ -14204,53 +30477,53 @@ struct meta_func { return node->id; } - /*! @copydoc meta_base::parent */ + /*! @copydoc meta_ctor::parent */ [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; /** - * @brief Returns the number of arguments accepted by a meta function. - * @return The number of arguments accepted by the meta function. + * @brief Returns the number of arguments accepted by a member function. + * @return The number of arguments accepted by the member function. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { - return node->size; + [[nodiscard]] size_type arity() const ENTT_NOEXCEPT { + return node->arity; } /** - * @brief Indicates whether a meta function is constant or not. - * @return True if the meta function is constant, false otherwise. + * @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->is_const; } /** - * @brief Indicates whether a meta function is static or not. - * @return True if the meta function is static, false otherwise. + * @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->is_static; } /** - * @brief Returns the meta type of the return type of a meta function. - * @return The meta type of the return type of the meta function. + * @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 meta type of the i-th argument of a meta function. - * @param index The index of the argument of which to return the meta type. - * @return The meta type of the i-th argument of a meta function, if any. + * @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(size_type index) const ENTT_NOEXCEPT; /** * @brief Invokes the underlying function, if possible. * - * To invoke a meta function, the parameters must be such that a cast or + * 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.
- * It must be possible to cast the instance to the parent type of the meta + * It must be possible to cast the instance to the parent type of the member * function. Otherwise, invoking the underlying function results in an * undefined behavior. * @@ -14259,8 +30532,8 @@ struct meta_func { * @param sz Number of parameters to use to invoke the function. * @return A meta any containing the returned value, if any. */ - [[nodiscard]] meta_any invoke(meta_handle instance, meta_any * const args, const std::size_t sz) const { - return sz == size() ? node->invoke(instance, args) : meta_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{}; } /** @@ -14275,8 +30548,8 @@ struct meta_func { */ template meta_any invoke(meta_handle instance, Args &&... args) const { - std::array arguments{std::forward(args)...}; - return invoke(instance, arguments.data(), sizeof...(Args)); + meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; + return invoke(std::move(instance), arguments, sizeof...(Args)); } /*! @copydoc meta_ctor::prop */ @@ -14290,13 +30563,12 @@ struct meta_func { * @return The property associated with the given key, if any. */ [[nodiscard]] meta_prop prop(meta_any key) const { - internal::meta_range range{node->prop}; - return std::find_if(range.begin(), range.end(), [&key](const auto &curr) { return curr.key() == key; }).operator->(); + return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return curr->id == key; }, node); } /** - * @brief Returns true if a meta object is valid, false otherwise. - * @return True if the meta object is valid, false otherwise. + * @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); @@ -14307,24 +30579,44 @@ private: }; -/*! @brief Opaque wrapper for meta types. */ +/*! @brief Opaque wrapper for types. */ class meta_type { - template - [[nodiscard]] auto ctor(std::index_sequence) const { - internal::meta_range range{node->ctor}; + static bool can_cast_or_convert(const internal::meta_type_node *type, const type_info info) ENTT_NOEXCEPT { + if(type->info == info) { + return true; + } - return std::find_if(range.begin(), range.end(), [](const auto &candidate) { - return candidate.size == sizeof...(Args) && ([](auto *from, auto *to) { - return (from->type_id == to->type_id) - || internal::find_if<&node_type::base>([to](const auto *curr) { return curr->type()->type_id == to->type_id; }, from) - || internal::find_if<&node_type::conv>([to](const auto *curr) { return curr->type()->type_id == to->type_id; }, from); - }(internal::meta_info::resolve(), candidate.arg(Indexes)) && ...); - }).operator->(); + for(const auto *curr = type->conv; curr; curr = curr->next) { + if(curr->type()->info == info) { + return true; + } + } + + for(const auto *curr = type->base; curr; curr = curr->next) { + if(auto *target = curr->type(); can_cast_or_convert(target, info)) { + return true; + } + } + + return false; + } + + template + [[nodiscard]] static const internal::meta_ctor_node * ctor(const internal::meta_ctor_node *curr, std::index_sequence) { + for(; curr; curr = curr->next) { + if(curr->arity == sizeof...(Args) && (can_cast_or_convert(internal::meta_info::resolve(), curr->arg(Index).info()) && ...)) { + return curr; + } + } + + return nullptr; } 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; @@ -14334,21 +30626,37 @@ public: {} /** - * @brief Returns the type id of the underlying type. - * @return The type id of the underlying type. + * @brief Constructs an instance from a given base node. + * @param curr The base node with which to construct the instance. */ - [[nodiscard]] id_type type_id() const ENTT_NOEXCEPT { - return node->type_id; + meta_type(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]] type_info info() const ENTT_NOEXCEPT { + return node->info; } /** - * @brief Returns the identifier assigned to a meta object. - * @return The identifier assigned to the meta object. + * @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 void or not. * @return True if the underlying type is void, false otherwise. @@ -14453,8 +30761,7 @@ public: /** * @brief Checks whether a type refers to a sequence container or not. - * @return True if the underlying type is a sequence container, false - * otherwise. + * @return True if the type is a sequence container, false otherwise. */ [[nodiscard]] bool is_sequence_container() const ENTT_NOEXCEPT { return node->is_sequence_container; @@ -14462,175 +30769,177 @@ public: /** * @brief Checks whether a type refers to an associative container or not. - * @return True if the underlying type is an associative container, false - * otherwise. + * @return True if the type is an associative container, false otherwise. */ [[nodiscard]] bool is_associative_container() const ENTT_NOEXCEPT { return node->is_associative_container; } /** - * @brief If a type refers to an array type, provides the number of - * dimensions of the array. - * @return The number of dimensions of the array if the underlying type is - * an array type, 0 otherwise. + * @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->template_info.is_template_specialization; + } + + /** + * @brief Returns the number of template arguments, if any. + * @return The number of template arguments, if any. + */ + [[nodiscard]] size_type template_arity() const ENTT_NOEXCEPT { + return node->template_info.arity; + } + + /** + * @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 is_template_specialization() ? node->template_info.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(size_type index) const ENTT_NOEXCEPT { + return index < template_arity() ? node->template_info.arg(index) : meta_type{}; + } + + /** + * @brief Provides the number of dimensions of an array type. + * @return The number of dimensions in case of array types, 0 otherwise. */ [[nodiscard]] size_type rank() const ENTT_NOEXCEPT { return node->rank; } /** - * @brief If a type refers to an array type, provides the number of elements - * along the given dimension of the array. + * @brief The number of elements along the given dimension of an array type. * @param dim The dimension of which to return the number of elements. - * @return The number of elements along the given dimension of the array if - * the underlying type is an array type, 0 otherwise. + * @return The number of elements along the given dimension in case of array + * types, 0 otherwise. */ [[nodiscard]] size_type extent(size_type dim = {}) const ENTT_NOEXCEPT { return node->extent(dim); } /** - * @brief Provides the meta type for which the pointer is defined. - * @return The meta type for which the pointer is defined or this meta type - * if it doesn't refer to a pointer type. + * @brief Provides the type for which the pointer is defined. + * @return The type for which the pointer is defined or this type if it + * doesn't refer to a pointer type. */ [[nodiscard]] meta_type remove_pointer() const ENTT_NOEXCEPT { return node->remove_pointer(); } /** - * @brief Provides the meta type for which the array is defined. - * @return The meta type for which the array is defined or this meta type - * if it doesn't refer to an array type. + * @brief Provides the type for which the array is defined. + * @return The type for which the array is defined or this type if it + * doesn't refer to an array type. */ [[nodiscard]] meta_type remove_extent() const ENTT_NOEXCEPT { return node->remove_extent(); } /** - * @brief Returns a range to use to visit top-level meta bases. - * @return An iterable range to use to visit top-level meta bases. + * @brief Returns a range to use to visit top-level base meta types. + * @return An iterable range to use to visit top-level base meta types. */ - [[nodiscard]] meta_range base() const ENTT_NOEXCEPT { + [[nodiscard]] meta_range base() const ENTT_NOEXCEPT { return node->base; } /** - * @brief Returns the meta base associated with a given identifier. + * @brief Returns the base meta type associated with a given identifier. * @param id Unique identifier. - * @return The meta base associated with the given identifier, if any. + * @return The base meta type associated with the given identifier, if any. */ - [[nodiscard]] meta_base base(const id_type id) const { - return internal::find_if<&node_type::base>([id](const auto *curr) { - return curr->type()->id == id; - }, node); + [[nodiscard]] meta_type base(const id_type id) const { + return internal::meta_visit<&node_type::base>([id](const auto *curr) { return curr->type()->id == id; }, node); } /** - * @brief Returns a range to use to visit top-level meta conversion - * functions. - * @return An iterable range to use to visit top-level meta conversion - * functions. - */ - [[nodiscard]] meta_range conv() const ENTT_NOEXCEPT { - return node->conv; - } - - /** - * @brief Returns the meta conversion function associated with a given type. - * @tparam Type The type to use to search for a meta conversion function. - * @return The meta conversion function associated with the given type, if - * any. - */ - template - [[nodiscard]] meta_conv conv() const { - return internal::find_if<&node_type::conv>([type_id = internal::meta_info::resolve()->type_id](const auto *curr) { - return curr->type()->type_id == type_id; - }, node); - } - - /** - * @brief Returns a range to use to visit top-level meta constructors. - * @return An iterable range to use to visit top-level meta constructors. + * @brief Returns a range to use to visit top-level constructors. + * @return An iterable range to use to visit top-level constructors. */ [[nodiscard]] meta_range ctor() const ENTT_NOEXCEPT { return node->ctor; } /** - * @brief Returns the meta constructor that accepts a given list of types of - * arguments. - * @return The requested meta constructor, if any. + * @brief Returns a constructor for a given list of types of arguments. + * @tparam Args Constructor arguments. + * @return The requested constructor, if any. */ template [[nodiscard]] meta_ctor ctor() const { - return ctor(std::index_sequence_for{}); + return ctor(node->ctor, std::make_index_sequence{}); } /** - * @brief Returns a range to use to visit top-level meta data. - * @return An iterable range to use to visit top-level meta data. + * @brief Returns a range to use to visit top-level data. + * @return An iterable range to use to visit top-level data. */ [[nodiscard]] meta_range data() const ENTT_NOEXCEPT { return node->data; } /** - * @brief Returns the meta data associated with a given identifier. + * @brief Returns the data associated with a given identifier. * - * The meta data of the base classes will also be visited, if any. + * The data of the base classes will also be visited, if any. * * @param id Unique identifier. - * @return The meta data associated with the given identifier, if any. + * @return The data associated with the given identifier, if any. */ [[nodiscard]] meta_data data(const id_type id) const { - return internal::find_if<&node_type::data>([id](const auto *curr) { - return curr->id == id; - }, node); + return internal::meta_visit<&node_type::data>([id](const auto *curr) { return curr->id == id; }, node); } /** - * @brief Returns a range to use to visit top-level meta functions. - * @return An iterable range to use to visit top-level meta functions. + * @brief Returns a range to use to visit top-level functions. + * @return An iterable range to use to visit top-level functions. */ [[nodiscard]] meta_range func() const ENTT_NOEXCEPT { return node->func; } /** - * @brief Returns the meta function associated with a given identifier. + * @brief Returns the function associated with a given identifier. * - * The meta functions of the base classes will also be visited, if any. + * The functions of the base classes will also be visited, if any.
+ * In the case of overloaded functions, the first one with the required + * identifier will be returned. * * @param id Unique identifier. - * @return The meta function associated with the given identifier, if any. + * @return The function associated with the given identifier, if any. */ [[nodiscard]] meta_func func(const id_type id) const { - return internal::find_if<&node_type::func>([id](const auto *curr) { - return curr->id == id; - }, node); + return internal::meta_visit<&node_type::func>([id](const auto *curr) { return curr->id == id; }, node); } /** * @brief Creates an instance of the underlying type, if possible. * - * To create a valid instance, 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. + * Parameters must be such that a cast or conversion to the required types + * is possible. Otherwise, an empty and thus invalid wrapper is returned. * * @param args Parameters to use to construct the instance. * @param sz Number of parameters to use to construct the instance. * @return A meta any containing the new instance, if any. */ - [[nodiscard]] meta_any construct(meta_any * const args, const std::size_t sz) const { - meta_any any{}; - - internal::find_if<&node_type::ctor>([args, sz, &any](const auto *curr) { - return (curr->size == sz) && (any = curr->invoke(args)); - }, node); - - return any; + [[nodiscard]] meta_any construct(meta_any * const args, const size_type sz) const { + meta_any ret{}; + internal::meta_visit<&node_type::ctor>([args, sz, &ret](const auto *curr) { return (curr->arity == sz) && (ret = curr->invoke(args)); }, node); + return ret; } /** @@ -14644,13 +30953,110 @@ public: */ template [[nodiscard]] meta_any construct(Args &&... args) const { - std::array arguments{std::forward(args)...}; - return construct(arguments.data(), sizeof...(Args)); + meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; + return construct(arguments, sizeof...(Args)); } /** - * @brief Returns a range to use to visit top-level meta properties. - * @return An iterable range to use to visit top-level meta properties. + * @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. Otherwise, invoking the underlying function results in an + * undefined behavior. + * + * @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 meta any 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 internal::meta_func_node* candidate{}; + size_type extent{sz + 1u}; + bool ambiguous{}; + + for(auto *it = internal::meta_visit<&node_type::func>([id, sz](const auto *curr) { return curr->id == id && curr->arity == sz; }, node); it && it->id == id && it->arity == sz; it = it->next) { + size_type direct{}; + size_type ext{}; + + for(size_type next{}; next < sz && next == (direct + ext); ++next) { + const auto type = args[next].type(); + const auto req = it->arg(next).info(); + type.info() == req ? ++direct : (ext += can_cast_or_convert(type.node, req)); + } + + if((direct + ext) == sz) { + if(ext < extent) { + candidate = it; + extent = ext; + ambiguous = false; + } else if(ext == extent) { + ambiguous = true; + } + } + } + + return (candidate && !ambiguous) ? 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 meta any containing the new instance, if any. + */ + template + meta_any invoke(const id_type id, meta_handle instance, Args &&... args) const { + meta_any arguments[sizeof...(Args) + 1u]{std::forward(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. Otherwise, invoking the setter results in an undefined + * behavior.
+ * The type of the value must be 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 + bool set(const id_type id, meta_handle instance, Type &&value) const { + auto const candidate = data(id); + return candidate ? candidate.set(std::move(instance), std::forward(value)) : false; + } + + /** + * @brief Gets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the data + * member. Otherwise, invoking the getter results in an undefined behavior. + * + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @return A meta any containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { + auto const candidate = data(id); + return candidate ? candidate.get(std::move(instance)) : meta_any{}; + } + + /** + * @brief Returns a range to use to visit top-level properties. + * @return An iterable range to use to visit top-level properties. */ [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { return node->prop; @@ -14665,43 +31071,40 @@ public: * @return The property associated with the given key, if any. */ [[nodiscard]] meta_prop prop(meta_any key) const { - return internal::find_if<&node_type::prop>([key = std::move(key)](const auto *curr) { - return curr->key() == key; - }, node); + return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return curr->id == key; }, node); } /** - * @brief Returns true if a meta object is valid, false otherwise. - * @return True if the meta object is valid, false otherwise. + * @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 meta objects refer to the same type. - * @param other The meta object with which to compare. - * @return True if the two meta objects refer to the same type, false - * otherwise. + * @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->type_id == other.node->type_id); + return (!node && !other.node) || (node && other.node && node->info == other.node->info); } /** - * @brief Resets a meta type and all its parts. + * @brief Resets a type and all its parts. * - * This function resets a meta type and all its data members, member - * functions and properties, as well as its constructors, destructors and - * conversion functions if any.
+ * This function resets a type and all its data members, member functions + * and properties, as well as its constructors, destructors and conversion + * functions if any.
* Base classes aren't reset but the link between the two types is removed. - * - * The meta type is also removed from the list of searchable types. + * + * The type is also removed from the list of searchable types. */ void reset() ENTT_NOEXCEPT { auto** it = internal::meta_context::global(); - while (*it && *it != node) { + while(*it && *it != node) { it = &(*it)->next; } @@ -14719,15 +31122,16 @@ public: } } }; - + unregister_all(&node->prop); unregister_all(&node->base); unregister_all(&node->conv); unregister_all(&node->ctor, &internal::meta_ctor_node::prop); unregister_all(&node->data, &internal::meta_data_node::prop); unregister_all(&node->func, &internal::meta_func_node::prop); - + node->id = {}; + node->ctor = node->def_ctor; node->dtor = nullptr; } @@ -14737,10 +31141,10 @@ private: /** - * @brief Checks if two meta objects refer to the same type. - * @param lhs A meta object, either valid or not. - * @param rhs A meta object, either valid or not. - * @return False if the two meta objects refer to the same node, true otherwise. + * @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); @@ -14752,23 +31156,31 @@ private: } -[[nodiscard]] inline meta_type meta_base::parent() const ENTT_NOEXCEPT { - return node->parent; +template +meta_any meta_any::invoke(const id_type id, Args &&... args) const { + return type().invoke(id, *this, std::forward(args)...); } -[[nodiscard]] inline meta_type meta_base::type() const ENTT_NOEXCEPT { - return node->type(); +template +meta_any meta_any::invoke(const id_type id, Args &&... args) { + return type().invoke(id, *this, std::forward(args)...); } -[[nodiscard]] inline meta_type meta_conv::parent() const ENTT_NOEXCEPT { - return node->parent; +template +bool meta_any::set(const id_type id, Type &&value) { + return type().set(id, *this, std::forward(value)); } -[[nodiscard]] inline meta_type meta_conv::type() const ENTT_NOEXCEPT { - return node->type(); +[[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); } @@ -14778,7 +31190,7 @@ private: [[nodiscard]] inline meta_type meta_ctor::arg(size_type index) const ENTT_NOEXCEPT { - return index < size() ? node->arg(index) : nullptr; + return index < arity() ? node->arg(index) : meta_type{}; } @@ -14803,26 +31215,28 @@ private: [[nodiscard]] inline meta_type meta_func::arg(size_type index) const ENTT_NOEXCEPT { - return index < size() ? node->arg(index) : nullptr; + return index < arity() ? node->arg(index) : meta_type{}; } -/*! @brief Opaque iterator for meta sequence containers. */ +/*! @brief Opaque iterator for sequence containers. */ class meta_sequence_container::meta_iterator { - /*! @brief A meta sequence container can access the underlying iterator. */ + /*! @brief A sequence container can access the underlying iterator. */ friend class meta_sequence_container; - template - static void incr(meta_any any) { - ++any.cast(); - } + enum class operation { INCR, DEREF }; + + using vtable_type = void(const operation, const any &, void *); template - [[nodiscard]] static meta_any deref(meta_any any) { - if constexpr(std::is_const_v())>>) { - return *any.cast(); - } else { - return std::ref(*any.cast()); + static void basic_vtable(const operation op, const any &from, void *to) { + switch(op) { + case operation::INCR: + ++any_cast(const_cast(from)); + break; + case operation::DEREF: + static_cast(to)->emplace::reference>(*any_cast(from)); + break; } } @@ -14848,14 +31262,13 @@ public: */ template meta_iterator(It iter) - : next_fn{&incr}, - get_fn{&deref}, + : vtable{&basic_vtable}, handle{std::move(iter)} {} /*! @brief Pre-increment operator. @return This iterator. */ meta_iterator & operator++() ENTT_NOEXCEPT { - return next_fn(handle.ref()), *this; + return vtable(operation::INCR, handle, nullptr), *this; } /*! @brief Post-increment operator. @return This iterator. */ @@ -14865,20 +31278,18 @@ public: } /** - * @brief Checks if two meta iterators refer to the same element. - * @param other The meta iterator with which to compare. - * @return True if the two meta iterators refer to the same element, false - * otherwise. + * @brief Checks if two iterators refer to the same element. + * @param other The iterator with which to compare. + * @return True if the iterators refer to the same element, false otherwise. */ [[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { return handle == other.handle; } /** - * @brief Checks if two meta iterators refer to the same element. - * @param other The meta iterator with which to compare. - * @return False if the two meta iterators refer to the same element, true - * otherwise. + * @brief Checks if two iterators refer to the same element. + * @param other The iterator with which to compare. + * @return False if the iterators refer to the same element, true otherwise. */ [[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { return !(*this == other); @@ -14886,10 +31297,12 @@ public: /** * @brief Indirection operator. - * @return The element to which the meta pointer points. + * @return The element to which the iterator points. */ [[nodiscard]] reference operator*() const { - return get_fn(handle.ref()); + meta_any other; + vtable(operation::DEREF, handle, &other); + return other; } /** @@ -14901,9 +31314,8 @@ public: } private: - void(* next_fn)(meta_any); - meta_any(* get_fn)(meta_any); - meta_any handle; + vtable_type *vtable{}; + any handle{}; }; @@ -14912,52 +31324,74 @@ struct meta_sequence_container::meta_sequence_container_proxy { using traits_type = meta_sequence_container_traits; [[nodiscard]] static meta_type value_type() ENTT_NOEXCEPT { - return internal::meta_info::resolve(); + return internal::meta_info::resolve(); } - [[nodiscard]] static size_type size(const void *container) ENTT_NOEXCEPT { - return traits_type::size(*static_cast(container)); + [[nodiscard]] static size_type size(const any &container) ENTT_NOEXCEPT { + return traits_type::size(any_cast(container)); } - [[nodiscard]] static bool resize(void *container, size_type sz) { - return traits_type::resize(*static_cast(container), sz); + [[nodiscard]] static bool resize(any &container, size_type sz) { + auto * const cont = any_cast(&container); + return cont ? traits_type::resize(*cont, sz) : false; } - [[nodiscard]] static bool clear(void *container) { - return traits_type::clear(*static_cast(container)); + [[nodiscard]] static bool clear(any &container) { + auto * const cont = any_cast(&container); + return cont ? traits_type::clear(*cont) : false; } - [[nodiscard]] static iterator begin(void *container) { - return iterator{traits_type::begin(*static_cast(container))}; + [[nodiscard]] static iterator begin(any &container) { + if(auto * const cont = any_cast(&container); cont) { + return iterator{traits_type::begin(*cont)}; + } + + return iterator{traits_type::cbegin(any_cast(container))}; } - [[nodiscard]] static iterator end(void *container) { - return iterator{traits_type::end(*static_cast(container))}; + [[nodiscard]] static iterator end(any &container) { + if(auto * const cont = any_cast(&container); cont) { + return iterator{traits_type::end(*cont)}; + } + + return iterator{traits_type::cend(any_cast(container))}; } - [[nodiscard]] static std::pair insert(void *container, iterator it, meta_any value) { - if(const auto *v_ptr = value.try_cast(); v_ptr || value.convert()) { - auto ret = traits_type::insert(*static_cast(container), it.handle.cast(), v_ptr ? *v_ptr : value.cast()); - return {iterator{std::move(ret.first)}, ret.second}; + [[nodiscard]] static std::pair insert(any &container, iterator it, meta_any &value) { + if(auto * const cont = any_cast(&container); cont) { + // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector + if(value.allow_cast() || value.allow_cast()) { + const auto *element = value.try_cast>(); + auto ret = traits_type::insert(*cont, any_cast(it.handle), element ? *element : value.cast()); + return { iterator{std::move(ret.first)}, ret.second }; + } } return {}; } - [[nodiscard]] static std::pair erase(void *container, iterator it) { - auto ret = traits_type::erase(*static_cast(container), it.handle.cast()); - return {iterator{std::move(ret.first)}, ret.second}; + [[nodiscard]] static std::pair erase(any &container, iterator it) { + if(auto * const cont = any_cast(&container); cont) { + auto ret = traits_type::erase(*cont, any_cast(it.handle)); + return { iterator{std::move(ret.first)}, ret.second }; + } + + return {}; } - [[nodiscard]] static meta_any get(void *container, size_type pos) { - return std::ref(traits_type::get(*static_cast(container), pos)); + [[nodiscard]] static meta_any get(any &container, size_type pos) { + if(auto * const cont = any_cast(&container); cont) { + return meta_any{std::in_place_type, traits_type::get(*cont, pos)}; + } + + return meta_any{std::in_place_type, traits_type::cget(any_cast(container), pos)}; } }; /** - * @brief Returns the value meta type of the wrapped container type. - * @return The value meta type of the wrapped container type. + * @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_fn(); @@ -14965,85 +31399,82 @@ struct meta_sequence_container::meta_sequence_container_proxy { /** - * @brief Returns the size of the wrapped container. - * @return The size of the wrapped container. + * @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(instance); + return size_fn(storage); } /** - * @brief Resizes the wrapped container to contain a given number of elements. + * @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(size_type sz) const { - return resize_fn(instance, sz); +inline bool meta_sequence_container::resize(size_type sz) { + return resize_fn(storage, sz); } /** - * @brief Clears the content of the wrapped container. + * @brief Clears the content of a container. * @return True in case of success, false otherwise. */ inline bool meta_sequence_container::clear() { - return clear_fn(instance); + return clear_fn(storage); } /** - * @brief Returns a meta iterator to the first element of the wrapped container. - * @return A meta iterator to the first element of the wrapped container. + * @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 begin_fn(instance); + return begin_fn(storage); } /** - * @brief Returns a meta iterator that is past the last element of the wrapped - * container. - * @return A meta iterator that is past the last element of the wrapped - * container. + * @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 end_fn(instance); + return end_fn(storage); } /** - * @brief Inserts an element at a specified location of the wrapped container. - * @param it Meta iterator before which the element will be inserted. + * @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 pair consisting of a meta iterator to the inserted element (in - * case of success) and a bool denoting whether the insertion took place. + * @return A pair consisting of an iterator to the inserted element (in case of + * success) and a bool denoting whether the insertion took place. */ inline std::pair meta_sequence_container::insert(iterator it, meta_any value) { - return insert_fn(instance, it, value.ref()); + return insert_fn(storage, it, value); } /** - * @brief Removes the specified element from the wrapped container. - * @param it Meta iterator to the element to remove. - * @return A pair consisting of a meta iterator following the last removed - * element (in case of success) and a bool denoting whether the insertion - * took place. + * @brief Removes a given element from a container. + * @param it Iterator to the element to remove. + * @return A pair consisting of an iterator following the last removed element + * (in case of success) and a bool denoting whether the insertion took place. */ inline std::pair meta_sequence_container::erase(iterator it) { - return erase_fn(instance, it); + return erase_fn(storage, it); } /** - * @brief Returns a reference to the element at a specified location of the - * wrapped container (no bounds checking is performed). + * @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[](size_type pos) { - return get_fn(instance, pos); + return get_fn(storage, pos); } @@ -15052,32 +31483,29 @@ inline std::pair meta_sequence_containe * @return False if the proxy is invalid, true otherwise. */ [[nodiscard]] inline meta_sequence_container::operator bool() const ENTT_NOEXCEPT { - return (instance != nullptr); + return static_cast(storage); } -/*! @brief Opaque iterator for meta associative containers. */ +/*! @brief Opaque iterator for associative containers. */ class meta_associative_container::meta_iterator { - template - static void incr(meta_any any) { - ++any.cast(); - } + enum operation { INCR, DEREF }; + + using vtable_type = void(const operation, const any &, void *); template - [[nodiscard]] static meta_any key(meta_any any) { - if constexpr(KeyOnly) { - return *any.cast(); - } else { - return any.cast()->first; - } - } - - template - [[nodiscard]] static meta_any value([[maybe_unused]] meta_any any) { - if constexpr(KeyOnly) { - return meta_any{}; - } else { - return std::ref(any.cast()->second); + static void basic_vtable(const operation op, const any &from, void *to) { + switch(op) { + case operation::INCR: + ++any_cast(const_cast(from)); + break; + case operation::DEREF: + if constexpr(KeyOnly) { + static_cast *>(to)->first = std::cref(*any_cast(from)); + } else { + *static_cast *>(to) = std::make_pair(std::cref(any_cast(from)->first), std::ref(any_cast(from)->second)); + } + break; } } @@ -15097,23 +31525,20 @@ public: meta_iterator() ENTT_NOEXCEPT = default; /** - * @brief Constructs a meta iterator from a given iterator. - * @tparam KeyOnly True if the associative container is also key-only, false - * otherwise. + * @brief Constructs an meta iterator from a given iterator. + * @tparam KeyOnly True if the container is also key-only, false otherwise. * @tparam It Type of actual iterator with which to build the meta iterator. * @param iter The actual iterator with which to build the meta iterator. */ template meta_iterator(std::integral_constant, It iter) - : next_fn{&incr}, - key_fn{&key}, - value_fn{&value}, + : vtable{&basic_vtable}, handle{std::move(iter)} {} /*! @brief Pre-increment operator. @return This iterator. */ meta_iterator & operator++() ENTT_NOEXCEPT { - return next_fn(handle.ref()), *this; + return vtable(operation::INCR, handle, nullptr), *this; } /*! @brief Post-increment operator. @return This iterator. */ @@ -15123,20 +31548,18 @@ public: } /** - * @brief Checks if two meta iterators refer to the same element. - * @param other The meta iterator with which to compare. - * @return True if the two meta iterators refer to the same element, false - * otherwise. + * @brief Checks if two iterators refer to the same element. + * @param other The iterator with which to compare. + * @return True if the iterators refer to the same element, false otherwise. */ [[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { return handle == other.handle; } /** - * @brief Checks if two meta iterators refer to the same element. - * @param other The meta iterator with which to compare. - * @return False if the two meta iterators refer to the same element, true - * otherwise. + * @brief Checks if two iterators refer to the same element. + * @param other The iterator with which to compare. + * @return False if the iterators refer to the same element, true otherwise. */ [[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { return !(*this == other); @@ -15144,10 +31567,12 @@ public: /** * @brief Indirection operator. - * @return The element to which the meta pointer points. + * @return The element to which the iterator points. */ [[nodiscard]] reference operator*() const { - return { key_fn(handle.ref()), value_fn(handle.ref()) }; + reference other; + vtable(operation::DEREF, handle, &other); + return other; } /** @@ -15159,10 +31584,8 @@ public: } private: - void(* next_fn)(meta_any); - meta_any(* key_fn)(meta_any); - meta_any(* value_fn)(meta_any); - meta_any handle; + vtable_type *vtable{}; + any handle{}; }; @@ -15171,44 +31594,53 @@ struct meta_associative_container::meta_associative_container_proxy { using traits_type = meta_associative_container_traits; [[nodiscard]] static meta_type key_type() ENTT_NOEXCEPT { - return internal::meta_info::resolve(); + return internal::meta_info::resolve(); } [[nodiscard]] static meta_type mapped_type() ENTT_NOEXCEPT { if constexpr(is_key_only_meta_associative_container_v) { return meta_type{}; } else { - return internal::meta_info::resolve(); + return internal::meta_info::resolve(); } } [[nodiscard]] static meta_type value_type() ENTT_NOEXCEPT { - return internal::meta_info::resolve(); + return internal::meta_info::resolve(); } - [[nodiscard]] static size_type size(const void *container) ENTT_NOEXCEPT { - return traits_type::size(*static_cast(container)); + [[nodiscard]] static size_type size(const any &container) ENTT_NOEXCEPT { + return traits_type::size(any_cast(container)); } - [[nodiscard]] static bool clear(void *container) { - return traits_type::clear(*static_cast(container)); + [[nodiscard]] static bool clear(any &container) { + auto * const cont = any_cast(&container); + return cont ? traits_type::clear(*cont) : false; } - [[nodiscard]] static iterator begin(void *container) { - return iterator{is_key_only_meta_associative_container{}, traits_type::begin(*static_cast(container))}; + [[nodiscard]] static iterator begin(any &container) { + if(auto * const cont = any_cast(&container); cont) { + return iterator{is_key_only_meta_associative_container{}, traits_type::begin(*cont)}; + } + + return iterator{is_key_only_meta_associative_container{}, traits_type::cbegin(any_cast(container))}; } - [[nodiscard]] static iterator end(void *container) { - return iterator{is_key_only_meta_associative_container{}, traits_type::end(*static_cast(container))}; + [[nodiscard]] static iterator end(any &container) { + if(auto * const cont = any_cast(&container); cont) { + return iterator{is_key_only_meta_associative_container{}, traits_type::end(*cont)}; + } + + return iterator{is_key_only_meta_associative_container{}, traits_type::cend(any_cast(container))}; } - [[nodiscard]] static bool insert(void *container, meta_any key, meta_any value) { - if(const auto *k_ptr = key.try_cast(); k_ptr || key.convert()) { + [[nodiscard]] static bool insert(any &container, meta_any &key, meta_any &value) { + if(auto * const cont = any_cast(&container); cont && key.allow_cast()) { if constexpr(is_key_only_meta_associative_container_v) { - return traits_type::insert(*static_cast(container), k_ptr ? *k_ptr : key.cast()); + return traits_type::insert(*cont, key.cast()); } else { - if(auto *m_ptr = value.try_cast(); m_ptr || value.convert()) { - return traits_type::insert(*static_cast(container), k_ptr ? *k_ptr : key.cast(), m_ptr ? *m_ptr : value.cast()); + if(value.allow_cast()) { + return traits_type::insert(*cont, key.cast(), value.cast()); } } } @@ -15216,17 +31648,21 @@ struct meta_associative_container::meta_associative_container_proxy { return false; } - [[nodiscard]] static bool erase(void *container, meta_any key) { - if(const auto *k_ptr = key.try_cast(); k_ptr || key.convert()) { - return traits_type::erase(*static_cast(container), k_ptr ? *k_ptr : key.cast()); + [[nodiscard]] static bool erase(any &container, meta_any &key) { + if(auto * const cont = any_cast(&container); cont && key.allow_cast()) { + return traits_type::erase(*cont, key.cast()); } return false; } - [[nodiscard]] static iterator find(void *container, meta_any key) { - if(const auto *k_ptr = key.try_cast(); k_ptr || key.convert()) { - return iterator{is_key_only_meta_associative_container{}, traits_type::find(*static_cast(container), k_ptr ? *k_ptr : key.cast())}; + [[nodiscard]] static iterator find(any &container, meta_any &key) { + if(key.allow_cast()) { + if(auto * const cont = any_cast(&container); cont) { + return iterator{is_key_only_meta_associative_container{}, traits_type::find(*cont, key.cast())}; + } + + return iterator{is_key_only_meta_associative_container{}, traits_type::cfind(any_cast(container), key.cast())}; } return {}; @@ -15235,8 +31671,7 @@ struct meta_associative_container::meta_associative_container_proxy { /** - * @brief Returns true if the associative container is also key-only, false - * otherwise. + * @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 { @@ -15245,8 +31680,8 @@ struct meta_associative_container::meta_associative_container_proxy { /** - * @brief Returns the key meta type of the wrapped container type. - * @return The key meta type of the wrapped container type. + * @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_fn(); @@ -15254,8 +31689,8 @@ struct meta_associative_container::meta_associative_container_proxy { /** - * @brief Returns the mapped meta type of the wrapped container type. - * @return The mapped meta type of the wrapped container type. + * @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_fn(); @@ -15270,57 +31705,56 @@ struct meta_associative_container::meta_associative_container_proxy { /*! @copydoc meta_sequence_container::size */ [[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const ENTT_NOEXCEPT { - return size_fn(instance); + return size_fn(storage); } /*! @copydoc meta_sequence_container::clear */ inline bool meta_associative_container::clear() { - return clear_fn(instance); + return clear_fn(storage); } /*! @copydoc meta_sequence_container::begin */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { - return begin_fn(instance); + return begin_fn(storage); } /*! @copydoc meta_sequence_container::end */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { - return end_fn(instance); + return end_fn(storage); } /** - * @brief Inserts an element (a key/value pair) into the wrapped container. + * @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(instance, key.ref(), value.ref()); + return insert_fn(storage, key, value); } /** - * @brief Removes the specified element from the wrapped container. + * @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(instance, key.ref()); + return erase_fn(storage, key); } /** - * @brief Returns an iterator to the element with key equivalent to a given - * one, if any. + * @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(instance, key.ref()); + return find_fn(storage, key); } @@ -15329,7 +31763,7 @@ inline bool meta_associative_container::erase(meta_any key) { * @return False if the proxy is invalid, true otherwise. */ [[nodiscard]] inline meta_associative_container::operator bool() const ENTT_NOEXCEPT { - return (instance != nullptr); + return static_cast(storage); } @@ -15338,6 +31772,8 @@ inline bool meta_associative_container::erase(meta_any key) { #endif +// #include "node.hpp" + // #include "policy.hpp" #ifndef ENTT_META_POLICY_HPP #define ENTT_META_POLICY_HPP @@ -15350,8 +31786,8 @@ namespace entt { struct as_ref_t {}; -/*! @brief Disambiguation tag. */ -inline constexpr as_ref_t as_ref; +/*! @brief Empty class type used to request the _as cref_ policy. */ +struct as_cref_t {}; /*! @brief Empty class type used to request the _as-is_ policy. */ @@ -15365,6 +31801,309 @@ struct as_void_t {}; } +#endif + +// #include "utility.hpp" +#ifndef ENTT_META_UTILITY_HPP +#define ENTT_META_UTILITY_HPP + + +#include +#include +#include +#include +// #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 +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 +struct meta_function_descriptor { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = std::conditional_t, type_list, type_list>; + + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr auto is_const = true; + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr auto is_static = !std::is_same_v; +}; + + +/** + * @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 +struct meta_function_descriptor { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = std::conditional_t, type_list, 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 = !std::is_same_v; +}; + + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Args Function arguments. + */ +template +struct meta_function_descriptor { + /*! @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 +class meta_function_helper { + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret(Class:: *)(Args...) const); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret(Class:: *)(Args...)); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret(*)(Args...)); + +public: + /*! @brief The meta function descriptor of the given function. */ + using type = decltype(get_rid_of_noexcept(std::declval())); +}; + + +/** + * @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 +using meta_function_helper_t = typename meta_function_helper::type; + + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Args Actual types of arguments. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] static meta_type meta_arg(type_list, const std::size_t index) ENTT_NOEXCEPT { + return internal::meta_arg_node(type_list{}, index); +} + + +/** + * @brief Constructs an instance given a list of erased parameters, if possible. + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @tparam Index Indexes to use to extract erased arguments from their list. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template +[[nodiscard]] meta_any meta_construct(meta_any * const args, std::index_sequence) { + if(((args+Index)->allow_cast() && ...)) { + return Type{(args+Index)->cast()...}; + } + + return {}; +} + + +/** + * @brief Sets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to set. + * @param instance An opaque instance of the underlying type, if required. + * @param value Parameter to use to set the variable. + * @return True in case of success, false otherwise. + */ +template +[[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { + if constexpr(!std::is_same_v && !std::is_same_v) { + if constexpr(std::is_function_v>> || std::is_member_function_pointer_v) { + using descriptor = meta_function_helper_t; + using data_type = type_list_element_t, typename descriptor::args_type>; + + if(auto * const clazz = instance->try_cast(); clazz) { + if(value.allow_cast()) { + std::invoke(Data, *clazz, value.cast()); + return true; + } + } + } else if constexpr(std::is_member_object_pointer_v) { + using data_type = std::remove_reference_t().*Data)>; + + if constexpr(!std::is_array_v && !std::is_const_v) { + if(auto * const clazz = instance->try_cast(); clazz) { + if(value.allow_cast()) { + std::invoke(Data, clazz) = value.cast(); + return true; + } + } + } + } else { + using data_type = std::remove_reference_t; + + if constexpr(!std::is_array_v && !std::is_const_v) { + if(value.allow_cast()) { + *Data = value.cast(); + 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 +[[nodiscard]] meta_any meta_getter([[maybe_unused]] meta_handle instance) { + [[maybe_unused]] auto dispatch = [](auto &&value) { + if constexpr(std::is_same_v) { + return meta_any{std::in_place_type, std::forward(value)}; + } else if constexpr(std::is_same_v) { + return meta_any{std::reference_wrapper{std::forward(value)}}; + } else if constexpr(std::is_same_v) { + return meta_any{std::cref(std::forward(value))}; + } else { + static_assert(std::is_same_v, "Policy not supported"); + return meta_any{std::forward(value)}; + } + }; + + if constexpr(std::is_function_v>> || std::is_member_function_pointer_v) { + auto * const clazz = instance->try_cast, const Type, Type>>(); + return clazz ? dispatch(std::invoke(Data, *clazz)) : meta_any{}; + } else if constexpr(std::is_member_object_pointer_v) { + if constexpr(std::is_array_v().*Data)>>>) { + return meta_any{}; + } else { + if(auto * clazz = instance->try_cast(); clazz) { + return dispatch(std::invoke(Data, *clazz)); + } else { + auto * fallback = instance->try_cast(); + return fallback ? dispatch(std::invoke(Data, *fallback)) : meta_any{}; + } + } + } else if constexpr(std::is_pointer_v) { + if constexpr(std::is_array_v>) { + return meta_any{}; + } else { + return dispatch(*Data); + } + } else { + return dispatch(Data); + } +} + + +/** + * @brief Invokes a function given a list of erased parameters, if possible. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Index Indexes to use to extract erased arguments from their list. + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, meta_any *args, std::index_sequence) { + using descriptor = meta_function_helper_t; + + auto dispatch = [](auto &&... params) { + if constexpr(std::is_void_v> || std::is_same_v) { + std::invoke(Candidate, std::forward(params)...); + return meta_any{std::in_place_type}; + } else if constexpr(std::is_same_v) { + return meta_any{std::reference_wrapper{std::invoke(Candidate, std::forward(params)...)}}; + } else if constexpr(std::is_same_v) { + return meta_any{std::cref(std::invoke(Candidate, std::forward(params)...))}; + } else { + static_assert(std::is_same_v, "Policy not supported"); + return meta_any{std::invoke(Candidate, std::forward(params)...)}; + } + }; + + if constexpr(std::is_invocable_v...>) { + if(const auto * const clazz = instance->try_cast(); clazz && ((args+Index)->allow_cast>() && ...)) { + return dispatch(*clazz, (args+Index)->cast>()...); + } + } else if constexpr(std::is_invocable_v...>) { + if(auto * const clazz = instance->try_cast(); clazz && ((args+Index)->allow_cast>() && ...)) { + return dispatch(*clazz, (args+Index)->cast>()...); + } + } else { + if(((args+Index)->allow_cast>() && ...)) { + return dispatch((args+Index)->cast>()...); + } + } + + return meta_any{}; +} + + +} + + #endif @@ -15381,160 +32120,18 @@ namespace entt { namespace internal { -template -struct meta_function_helper; - - -template -struct meta_function_helper { - using return_type = std::remove_cv_t>; - using args_type = std::tuple>...>; - - static constexpr auto is_const = Const; - - [[nodiscard]] static auto arg(typename internal::meta_func_node::size_type index) ENTT_NOEXCEPT { - return std::array{{meta_info::resolve()...}}[index]; - } -}; - - -template -constexpr meta_function_helper -to_meta_function_helper(Ret(Class:: *)(Args...) const); - - -template -constexpr meta_function_helper -to_meta_function_helper(Ret(Class:: *)(Args...)); - - -template -constexpr meta_function_helper -to_meta_function_helper(Ret(*)(Args...)); - - -constexpr void to_meta_function_helper(...); - - -template -using meta_function_helper_t = decltype(to_meta_function_helper(std::declval())); - - -template -[[nodiscard]] meta_any construct(meta_any * const args, std::index_sequence) { - [[maybe_unused]] auto direct = std::make_tuple((args+Indexes)->try_cast()...); - return ((std::get(direct) || (args+Indexes)->convert()) && ...) - ? Type{(std::get(direct) ? *std::get(direct) : (args+Indexes)->cast())...} - : meta_any{}; +template +[[nodiscard]] bool find_if(const Node *candidate, const Node *node) ENTT_NOEXCEPT { + return node && (node == candidate || find_if(candidate, node->next)); } -template -[[nodiscard]] bool setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { - bool accepted = false; - - if constexpr(std::is_function_v>> || std::is_member_function_pointer_v) { - using helper_type = meta_function_helper_t; - using data_type = std::tuple_element_t, typename helper_type::args_type>; - - if(auto * const clazz = instance->try_cast(); clazz) { - if(auto * const direct = value.try_cast(); direct || value.convert()) { - std::invoke(Data, *clazz, direct ? *direct : value.cast()); - accepted = true; - } - } - } else if constexpr(std::is_member_object_pointer_v) { - using data_type = std::remove_cv_t().*Data)>>; - - if constexpr(!std::is_array_v) { - if(auto * const clazz = instance->try_cast(); clazz) { - if(auto * const direct = value.try_cast(); direct || value.convert()) { - std::invoke(Data, clazz) = (direct ? *direct : value.cast()); - accepted = true; - } - } - } +template +[[nodiscard]] bool find_if_not(const Id id, Node *node, const Node *owner) ENTT_NOEXCEPT { + if constexpr(std::is_pointer_v) { + return node && ((*node->id == *id && node != owner) || find_if_not(id, node->next, owner)); } else { - using data_type = std::remove_cv_t>; - - if constexpr(!std::is_array_v) { - if(auto * const direct = value.try_cast(); direct || value.convert()) { - *Data = (direct ? *direct : value.cast()); - accepted = true; - } - } - } - - return accepted; -} - - -template -[[nodiscard]] meta_any getter([[maybe_unused]] meta_handle instance) { - [[maybe_unused]] auto dispatch = [](auto &&value) { - if constexpr(std::is_same_v) { - return meta_any{std::in_place_type, std::forward(value)}; - } else if constexpr(std::is_same_v) { - return meta_any{std::ref(std::forward(value))}; - } else { - static_assert(std::is_same_v, "Policy not supported"); - return meta_any{std::forward(value)}; - } - }; - - if constexpr(std::is_function_v>> || std::is_member_function_pointer_v) { - auto * const clazz = instance->try_cast(); - return clazz ? dispatch(std::invoke(Data, *clazz)) : meta_any{}; - } else if constexpr(std::is_member_object_pointer_v) { - if constexpr(std::is_array_v().*Data)>>>) { - return meta_any{}; - } else { - auto * const clazz = instance->try_cast(); - return clazz ? dispatch(std::invoke(Data, clazz)) : meta_any{}; - } - } else if constexpr(std::is_pointer_v>) { - if constexpr(std::is_array_v>) { - return meta_any{}; - } else { - return dispatch(*Data); - } - } else { - return dispatch(Data); - } -} - - -template -[[nodiscard]] meta_any invoke([[maybe_unused]] meta_handle instance, meta_any *args, std::index_sequence) { - using helper_type = meta_function_helper_t; - - auto dispatch = [](auto *... params) { - if constexpr(std::is_void_v || std::is_same_v) { - std::invoke(Candidate, *params...); - return meta_any{std::in_place_type}; - } else if constexpr(std::is_same_v) { - return meta_any{std::ref(std::invoke(Candidate, *params...))}; - } else { - static_assert(std::is_same_v, "Policy not supported"); - return meta_any{std::invoke(Candidate, *params...)}; - } - }; - - [[maybe_unused]] const auto direct = std::make_tuple([](meta_any *any, auto *value) { - using arg_type = std::remove_reference_t; - - if(!value && any->convert()) { - value = any->try_cast(); - } - - return value; - }(args+Indexes, (args+Indexes)->try_cast>())...); - - if constexpr(std::is_function_v>>) { - return (std::get(direct) && ...) ? dispatch(std::get(direct)...) : meta_any{}; - } else { - auto * const clazz = instance->try_cast(); - return (clazz && (std::get(direct) && ...)) ? dispatch(clazz, std::get(direct)...) : meta_any{}; + return node && ((node->id == id && node != owner) || find_if_not(id, node->next, owner)); } } @@ -15557,7 +32154,7 @@ template * there are no subtle errors at runtime. */ template -class meta_factory; +struct meta_factory; /** @@ -15566,11 +32163,8 @@ class meta_factory; * @tparam Spec Property specialization pack used to disambiguate overloads. */ template -class meta_factory: public meta_factory { - [[nodiscard]] bool exists(const meta_any &key, const internal::meta_prop_node *node) ENTT_NOEXCEPT { - return node && (node->key() == key || exists(key, node->next)); - } - +struct meta_factory: public meta_factory { +private: template void unpack(std::index_sequence, std::tuple property, Other &&... other) { unroll(choice<3>, std::move(std::get(property))..., std::forward(other)...); @@ -15602,27 +32196,25 @@ class meta_factory: public meta_factory { template void unroll(choice_t<0>) {} - template - void assign(Key &&key, Value &&... value) { - static const auto property{std::make_tuple(std::forward(key), std::forward(value)...)}; + template + void assign(Key &&key, meta_any value = {}) { + static meta_any property[2u]{}; static internal::meta_prop_node node{ nullptr, - []() -> meta_any { - return std::get<0>(property); - }, - []() -> meta_any { - if constexpr(sizeof...(Value) == 0) { - return {}; - } else { - return std::get<1>(property); - } - } + property[0u], + property[1u] }; - ENTT_ASSERT(!exists(node.key(), *curr)); - node.next = *curr; - *curr = &node; + entt::meta_any instance{std::forward(key)}; + ENTT_ASSERT(!internal::find_if_not(&instance, *curr, &node)); + property[0u] = std::move(instance); + property[1u] = std::move(value); + + if(!internal::find_if(&node, *curr)) { + node.next = *curr; + *curr = &node; + } } public: @@ -15682,31 +32274,22 @@ private: * @tparam Type Reflected type for which the factory was created. */ template -class meta_factory { - template - bool exists(const Node *candidate, const Node *node) ENTT_NOEXCEPT { - return node && (node == candidate || exists(candidate, node->next)); - } - - template - bool exists(const id_type id, const Node *node) ENTT_NOEXCEPT { - return node && (node->id == id || exists(id, node->next)); - } - -public: +struct meta_factory { /** * @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_info::id()) { + auto type(const id_type id = type_hash::value()) { auto * const node = internal::meta_info::resolve(); - ENTT_ASSERT(!exists(id, *internal::meta_context::global())); - ENTT_ASSERT(!exists(node, *internal::meta_context::global())); + ENTT_ASSERT(!internal::find_if_not(id, *internal::meta_context::global(), node)); node->id = id; - node->next = *internal::meta_context::global(); - *internal::meta_context::global() = node; + + if(!internal::find_if(node, *internal::meta_context::global())) { + node->next = *internal::meta_context::global(); + *internal::meta_context::global() = node; + } return meta_factory{&node->prop}; } @@ -15733,9 +32316,10 @@ public: } }; - ENTT_ASSERT(!exists(&node, type->base)); - node.next = type->base; - type->base = &node; + if(!internal::find_if(&node, type->base)) { + node.next = type->base; + type->base = &node; + } return meta_factory{}; } @@ -15763,9 +32347,10 @@ public: } }; - ENTT_ASSERT(!exists(&node, type->conv)); - node.next = type->conv; - type->conv = &node; + if(!internal::find_if(&node, type->conv)) { + node.next = type->conv; + type->conv = &node; + } return meta_factory{}; } @@ -15796,9 +32381,10 @@ public: } }; - ENTT_ASSERT(!exists(&node, type->conv)); - node.next = type->conv; - type->conv = &node; + if(!internal::find_if(&node, type->conv)) { + node.next = type->conv; + type->conv = &node; + } return meta_factory{}; } @@ -15806,38 +32392,41 @@ public: /** * @brief Assigns a meta constructor to a meta type. * - * Free functions 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.
+ * 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.
* From a client's point of view, nothing changes if a constructor of a meta - * type is a built-in one or a free function. + * type is a built-in one or not. * - * @tparam Func The actual function to use as a constructor. + * @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 + template auto ctor() ENTT_NOEXCEPT { - using helper_type = internal::meta_function_helper_t; - static_assert(std::is_same_v, "The function doesn't return an object of the required type"); + using descriptor = meta_function_helper_t; + static_assert(std::is_same_v, Type>, "The function doesn't return an object of the required type"); auto * const type = internal::meta_info::resolve(); static internal::meta_ctor_node node{ type, nullptr, nullptr, - std::tuple_size_v, - &helper_type::arg, - [](meta_any * const any) { - return internal::invoke({}, any, std::make_index_sequence>{}); + descriptor::args_type::size, + [](const typename internal::meta_ctor_node::size_type index) ENTT_NOEXCEPT { + return meta_arg(typename descriptor::args_type{}, index); + }, + [](meta_any * const args) { + return meta_invoke({}, args, std::make_index_sequence{}); } }; - ENTT_ASSERT(!exists(&node, type->ctor)); - node.next = type->ctor; - type->ctor = &node; + if(!internal::find_if(&node, type->ctor)) { + node.next = type->ctor; + type->ctor = &node; + } - return meta_factory>{&node.prop}; + return meta_factory>{&node.prop}; } /** @@ -15852,23 +32441,26 @@ public: */ template auto ctor() ENTT_NOEXCEPT { - using helper_type = internal::meta_function_helper_t; + using descriptor = meta_function_helper_t; auto * const type = internal::meta_info::resolve(); static internal::meta_ctor_node node{ type, nullptr, nullptr, - std::tuple_size_v, - &helper_type::arg, - [](meta_any * const any) { - return internal::construct>...>(any, std::make_index_sequence>{}); + descriptor::args_type::size, + [](const typename internal::meta_ctor_node::size_type index) ENTT_NOEXCEPT { + return meta_arg(typename descriptor::args_type{}, index); + }, + [](meta_any * const args) { + return meta_construct(args, std::make_index_sequence{}); } }; - ENTT_ASSERT(!exists(&node, type->ctor)); - node.next = type->ctor; - type->ctor = &node; + if(!internal::find_if(&node, type->ctor)) { + node.next = type->ctor; + type->ctor = &node; + } return meta_factory{&node.prop}; } @@ -15894,12 +32486,8 @@ public: static_assert(std::is_invocable_v, "The function doesn't accept an object of the type provided"); auto * const type = internal::meta_info::resolve(); - ENTT_ASSERT(!type->dtor); - type->dtor = [](void *instance) { - if(instance) { - std::invoke(Func, *static_cast(instance)); - } + std::invoke(Func, *static_cast(instance)); }; return meta_factory{}; @@ -15923,7 +32511,7 @@ public: if constexpr(std::is_member_object_pointer_v) { return data(id); } else { - using data_type = std::remove_pointer_t>; + using data_type = std::remove_pointer_t; auto * const type = internal::meta_info::resolve(); static internal::meta_data_node node{ @@ -15931,23 +32519,20 @@ public: type, nullptr, nullptr, + std::is_same_v || std::is_const_v, true, &internal::meta_info::resolve, - []() -> std::remove_const_t { - if constexpr(std::is_same_v || std::is_const_v) { - return nullptr; - } else { - return &internal::setter; - } - }(), - &internal::getter + &meta_setter, + &meta_getter }; - ENTT_ASSERT(!exists(id, type->data)); - ENTT_ASSERT(!exists(&node, type->data)); + ENTT_ASSERT(!internal::find_if_not(id, type->data, &node)); node.id = id; - node.next = type->data; - type->data = &node; + + if(!internal::find_if(&node, type->data)) { + node.next = type->data; + type->data = &node; + } return meta_factory>{&node.prop}; } @@ -15983,23 +32568,20 @@ public: type, nullptr, nullptr, + std::is_same_v || (std::is_member_object_pointer_v && std::is_const_v), false, &internal::meta_info::resolve, - []() -> std::remove_const_t { - if constexpr(std::is_same_v || (std::is_member_object_pointer_v && std::is_const_v)) { - return nullptr; - } else { - return &internal::setter; - } - }(), - &internal::getter + &meta_setter, + &meta_getter }; - ENTT_ASSERT(!exists(id, type->data)); - ENTT_ASSERT(!exists(&node, type->data)); + ENTT_ASSERT(!internal::find_if_not(id, type->data, &node)); node.id = id; - node.next = type->data; - type->data = &node; + + if(!internal::find_if(&node, type->data)) { + node.next = type->data; + type->data = &node; + } return meta_factory, std::integral_constant>{&node.prop}; } @@ -16019,7 +32601,7 @@ public: */ template auto func(const id_type id) ENTT_NOEXCEPT { - using helper_type = internal::meta_function_helper_t; + using descriptor = meta_function_helper_t; auto * const type = internal::meta_info::resolve(); static internal::meta_func_node node{ @@ -16027,21 +32609,32 @@ public: type, nullptr, nullptr, - std::tuple_size_v, - helper_type::is_const, - !std::is_member_function_pointer_v, - &internal::meta_info, void, typename helper_type::return_type>>::resolve, - &helper_type::arg, + descriptor::args_type::size, + descriptor::is_const, + descriptor::is_static, + &internal::meta_info, void, typename descriptor::return_type>>::resolve, + [](const typename internal::meta_func_node::size_type index) ENTT_NOEXCEPT { + return meta_arg(typename descriptor::args_type{}, index); + }, [](meta_handle instance, meta_any *args) { - return internal::invoke(*instance, args, std::make_index_sequence>{}); + return meta_invoke(std::move(instance), args, std::make_index_sequence{}); } }; - ENTT_ASSERT(!exists(id, type->func)); - ENTT_ASSERT(!exists(&node, type->func)); + for(auto *it = &type->func; *it; it = &(*it)->next) { + if(*it == &node) { + *it = node.next; + break; + } + } + + internal::meta_func_node **it = &type->func; + for(; *it && (*it)->id != id; it = &(*it)->next); + for(; *it && (*it)->id == id && (*it)->arity < node.arity; it = &(*it)->next); + node.id = id; - node.next = type->func; - type->func = &node; + node.next = *it; + *it = &node; return meta_factory>{&node.prop}; } @@ -16072,14 +32665,2520 @@ template #endif -// #include "meta/internal.hpp" - // #include "meta/meta.hpp" +#ifndef ENTT_META_META_HPP +#define ENTT_META_META_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/any.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/utility.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "adl_pointer.hpp" + +// #include "ctx.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 { + template + struct meta_sequence_container_proxy; + + 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 + meta_sequence_container(std::in_place_type_t, any instance) ENTT_NOEXCEPT + : value_type_fn{&meta_sequence_container_proxy::value_type}, + size_fn{&meta_sequence_container_proxy::size}, + resize_fn{&meta_sequence_container_proxy::resize}, + clear_fn{&meta_sequence_container_proxy::clear}, + begin_fn{&meta_sequence_container_proxy::begin}, + end_fn{&meta_sequence_container_proxy::end}, + insert_fn{&meta_sequence_container_proxy::insert}, + erase_fn{&meta_sequence_container_proxy::erase}, + get_fn{&meta_sequence_container_proxy::get}, + storage{std::move(instance)} + {} + + [[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; + [[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; + inline bool resize(size_type); + inline bool clear(); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline std::pair insert(iterator, meta_any); + inline std::pair erase(iterator); + [[nodiscard]] inline meta_any operator[](size_type); + [[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; + +private: + meta_type(* value_type_fn)() ENTT_NOEXCEPT = nullptr; + size_type(* size_fn)(const any &) ENTT_NOEXCEPT = nullptr; + bool(* resize_fn)(any &, size_type) = nullptr; + bool(* clear_fn)(any &) = nullptr; + iterator(* begin_fn)(any &) = nullptr; + iterator(* end_fn)(any &) = nullptr; + std::pair(* insert_fn)(any &, iterator, meta_any &) = nullptr; + std::pair(* erase_fn)(any &, iterator) = nullptr; + meta_any(* get_fn)(any &, size_type) = nullptr; + any storage{}; +}; + + +/*! @brief Proxy object for associative containers. */ +class meta_associative_container { + template + struct meta_associative_container_proxy; + + 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 + meta_associative_container(std::in_place_type_t, any instance) ENTT_NOEXCEPT + : key_only_container{is_key_only_meta_associative_container_v}, + key_type_fn{&meta_associative_container_proxy::key_type}, + mapped_type_fn{&meta_associative_container_proxy::mapped_type}, + value_type_fn{&meta_associative_container_proxy::value_type}, + size_fn{&meta_associative_container_proxy::size}, + clear_fn{&meta_associative_container_proxy::clear}, + begin_fn{&meta_associative_container_proxy::begin}, + end_fn{&meta_associative_container_proxy::end}, + insert_fn{&meta_associative_container_proxy::insert}, + erase_fn{&meta_associative_container_proxy::erase}, + find_fn{&meta_associative_container_proxy::find}, + storage{std::move(instance)} + {} + + [[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{}; + meta_type(* key_type_fn)() ENTT_NOEXCEPT = nullptr; + meta_type(* mapped_type_fn)() ENTT_NOEXCEPT = nullptr; + meta_type(* value_type_fn)() ENTT_NOEXCEPT = nullptr; + size_type(* size_fn)(const any &) ENTT_NOEXCEPT = nullptr; + bool(* clear_fn)(any &) = nullptr; + iterator(* begin_fn)(any &) = nullptr; + iterator(* end_fn)(any &) = 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 { DTOR, REF, CREF, DEREF, CDEREF, SEQ, CSEQ, ASSOC, CASSOC }; + + using vtable_type = void(const operation, const any &, void *); + + template + static void basic_vtable(const operation op, [[maybe_unused]] const any &from, [[maybe_unused]] void *to) { + if constexpr(!std::is_void_v) { + switch(op) { + case operation::DTOR: + if constexpr(!std::is_lvalue_reference_v) { + if(auto *curr = static_cast(to); curr->dtor) { + curr->dtor(const_cast(from).data()); + } + } + break; + case operation::REF: + case operation::CREF: + *static_cast(to) = (op == operation::REF ? meta_any{std::ref(any_cast(const_cast(from)))} : meta_any{std::cref(any_cast &>(from))}); + break; + case operation::DEREF: + case operation::CDEREF: + if constexpr(is_meta_pointer_like_v>) { + using element_type = std::remove_const_t>::element_type>; + + if constexpr(std::is_function_v) { + *static_cast(to) = any_cast>(from); + } else if constexpr(!std::is_same_v) { + using adl_meta_pointer_like_type = adl_meta_pointer_like>; + + if constexpr(std::is_lvalue_reference_v &>()))>) { + auto &&obj = adl_meta_pointer_like_type::dereference(any_cast &>(from)); + *static_cast(to) = (op == operation::DEREF ? meta_any{std::ref(obj)} : meta_any{std::cref(obj)}); + } else { + *static_cast(to) = adl_meta_pointer_like_type::dereference(any_cast &>(from)); + } + } + } + break; + case operation::SEQ: + case operation::CSEQ: + if constexpr(is_complete_v>>) { + *static_cast(to) = { std::in_place_type>, (op == operation::SEQ ? const_cast(from).as_ref() : from.as_ref()) }; + } + break; + case operation::ASSOC: + case operation::CASSOC: + if constexpr(is_complete_v>>) { + *static_cast(to) = { std::in_place_type>, (op == operation::ASSOC ? const_cast(from).as_ref() : from.as_ref()) }; + } + break; + } + } + } + +public: + /*! @brief Default constructor. */ + meta_any() ENTT_NOEXCEPT + : storage{}, + node{}, + vtable{&basic_vtable} + {} + + /** + * @brief Constructs a meta any by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit meta_any(std::in_place_type_t, Args &&... args) + : storage{std::in_place_type, std::forward(args)...}, + node{internal::meta_info::resolve()}, + vtable{&basic_vtable} + {} + + /** + * @brief Constructs a meta any that holds an unmanaged object. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template + meta_any(std::reference_wrapper value) + : meta_any{std::in_place_type, value.get()} + {} + + /** + * @brief Constructs a meta any from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, meta_any>>> + meta_any(Type &&value) + : meta_any{std::in_place_type>, std::forward(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)} + {} + + /*! @brief Frees the internal storage, whatever it means. */ + ~meta_any() { + vtable(operation::DTOR, storage, node); + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This meta any object. + */ + meta_any & operator=(const meta_any &other) { + std::exchange(vtable, other.vtable)(operation::DTOR, storage, node); + 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) { + std::exchange(vtable, std::exchange(other.vtable, &basic_vtable))(operation::DTOR, storage, node); + 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 + meta_any & operator=(std::reference_wrapper value) { + emplace(value.get()); + 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 + std::enable_if_t, meta_any>, meta_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns the type of the underlying object. + * @return The type of the underlying object, if any. + */ + [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; + + /** + * @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 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 meta any containing the returned value, if any. + */ + template + meta_any invoke(const id_type id, Args &&... args) const; + + /*! @copydoc invoke */ + template + meta_any invoke(const id_type id, Args &&... args); + + /** + * @brief Sets the value of a given variable. + * + * The type of the value must be 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 + bool set(const id_type id, Type &&value); + + /** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @return A meta any 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 + [[nodiscard]] const Type * try_cast() const { + if(node) { + if(const auto info = type_id(); node->info == info) { + return any_cast(&storage); + } else if(const auto *base = internal::meta_visit<&internal::meta_type_node::base>([info](const auto *curr) { return curr->type()->info == info; }, node); base) { + return static_cast(base->cast(storage.data())); + } + } + + return nullptr; + } + + /*! @copydoc try_cast */ + template + [[nodiscard]] Type * try_cast() { + if(node) { + if(const auto info = type_id(); node->info == info) { + return any_cast(&storage); + } else if(const auto *base = internal::meta_visit<&internal::meta_type_node::base>([info](const auto *curr) { return curr->type()->info == info; }, node); base) { + return static_cast(const_cast *>(base->cast(static_cast &>(storage).data()))); + } + } + + 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 in undefined behavior. + * + * @tparam Type Type to which to cast the instance. + * @return A reference to the contained instance. + */ + template + [[nodiscard]] Type cast() const { + auto * const actual = try_cast>(); + ENTT_ASSERT(actual); + return static_cast(*actual); + } + + /*! @copydoc cast */ + template + [[nodiscard]] Type cast() { + // forces const on non-reference types to make them work also with wrappers for const references + auto * const instance = try_cast>(); + ENTT_ASSERT(instance); + return static_cast(*instance); + } + + /** + * @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 + [[nodiscard]] meta_any allow_cast() const { + if(try_cast>() != nullptr) { + return as_ref(); + } else if(node) { + if(const auto * const conv = internal::meta_visit<&internal::meta_type_node::conv>([info = type_id()](const auto *curr) { return curr->type()->info == info; }, node); conv) { + return conv->conv(storage.data()); + } + } + + return {}; + } + + /** + * @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 + bool allow_cast() { + // forces const on non-reference types to make them work also with wrappers for const references + if(try_cast>() != nullptr) { + return true; + } else if(node) { + if(const auto * const conv = internal::meta_visit<&internal::meta_type_node::conv>([info = type_id()](const auto *curr) { return curr->type()->info == info; }, node); conv) { + *this = conv->conv(std::as_const(storage).data()); + return true; + } + } + + return false; + } + + /** + * @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 + void emplace(Args &&... args) { + std::exchange(vtable, &basic_vtable)(operation::DTOR, storage, node); + storage.emplace(std::forward(args)...); + node = internal::meta_info::resolve(); + } + + /*! @brief Destroys contained object */ + void reset() { + std::exchange(vtable, &basic_vtable)(operation::DTOR, storage, node); + 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 { + meta_sequence_container proxy; + vtable(operation::SEQ, storage, &proxy); + return proxy; + } + + /*! @copydoc as_sequence_container */ + [[nodiscard]] meta_sequence_container as_sequence_container() const ENTT_NOEXCEPT { + meta_sequence_container proxy; + vtable(operation::CSEQ, storage, &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 { + meta_associative_container proxy; + vtable(operation::ASSOC, storage, &proxy); + return proxy; + } + + /*! @copydoc as_associative_container */ + [[nodiscard]] meta_associative_container as_associative_container() const ENTT_NOEXCEPT { + meta_associative_container proxy; + vtable(operation::CASSOC, storage, &proxy); + return proxy; + } + + /** + * @brief Indirection operator for dereferencing opaque objects. + * @return A meta any that shares a reference to an unmanaged object if the + * wrapped element is dereferenceable, an invalid meta any otherwise. + */ + [[nodiscard]] meta_any operator*() ENTT_NOEXCEPT { + meta_any ret{}; + vtable(operation::DEREF, storage, &ret); + return ret; + } + + /*! @copydoc operator* */ + [[nodiscard]] meta_any operator*() const ENTT_NOEXCEPT { + meta_any ret{}; + vtable(operation::CDEREF, 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); + } + + /** + * @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. + */ + [[nodiscard]] bool operator==(const meta_any &other) const { + return (!node && !other.node) || (node && other.node && node->info == other.node->info && storage == other.storage); + } + + /** + * @brief Aliasing constructor. + * @return A meta any that shares a reference to an unmanaged object. + */ + [[nodiscard]] meta_any as_ref() ENTT_NOEXCEPT { + meta_any ref{}; + vtable(operation::REF, storage, &ref); + return ref; + } + + /*! @copydoc as_ref */ + [[nodiscard]] meta_any as_ref() const ENTT_NOEXCEPT { + meta_any ref{}; + vtable(operation::CREF, storage, &ref); + return ref; + } + +private: + any storage; + internal::meta_type_node *node; + vtable_type *vtable; +}; + + +/** + * @brief Checks if two wrappers differ in their content. + * @param lhs A meta any object, either empty or not. + * @param rhs A meta any object, 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 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.
+ * 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, meta_handle>>> + meta_handle(Type &value) ENTT_NOEXCEPT + : meta_handle{} + { + if constexpr(std::is_same_v, meta_any>) { + any = value.as_ref(); + } else { + any.emplace(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(any); + } + + /** + * @brief Access operator for accessing the contained opaque object. + * @return A meta any 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 meta any 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 meta any 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 constructors. */ +struct meta_ctor { + /*! @brief Node type. */ + using node_type = internal::meta_ctor_node; + /*! @brief Unsigned integer type. */ + using size_type = typename node_type::size_type; + + /*! @copydoc meta_prop::meta_prop */ + meta_ctor(const node_type *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /** + * @brief Returns the type to which an object belongs. + * @return The type to which the object belongs. + */ + [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; + + /** + * @brief Returns the number of arguments accepted by a constructor. + * @return The number of arguments accepted by the constructor. + */ + [[nodiscard]] size_type arity() const ENTT_NOEXCEPT { + return node->arity; + } + + /** + * @brief Returns the type of the i-th argument of a constructor. + * @param index Index of the argument of which to return the type. + * @return The type of the i-th argument of a constructor. + */ + [[nodiscard]] meta_type arg(size_type index) const ENTT_NOEXCEPT; + + /** + * @brief Creates an instance of the underlying type, if possible. + * + * Parameters must be such that a cast or conversion to the required types + * is possible. Otherwise, an empty and thus invalid wrapper is returned. + * + * @param args Parameters to use to construct the instance. + * @param sz Number of parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ + [[nodiscard]] meta_any invoke(meta_any * const args, const size_type sz) const { + return sz == arity() ? node->invoke(args) : meta_any{}; + } + + /** + * @copybrief invoke + * + * @sa invoke + * + * @tparam Args Types of arguments to use to construct the instance. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ + template + [[nodiscard]] meta_any invoke([[maybe_unused]] Args &&... args) const { + meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; + return invoke(arguments, sizeof...(Args)); + } + + /** + * @brief Returns a range to use to visit all properties. + * @return An iterable range to use to visit all properties. + */ + [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { + return node->prop; + } + + /** + * @brief Returns the property associated with a given key. + * @param key The key to use to search for a property. + * @return The property associated with the given key, if any. + */ + [[nodiscard]] meta_prop prop(meta_any key) const { + return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return curr->id == 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); + } + +private: + const node_type *node; +}; + + +/*! @brief Opaque wrapper for data members. */ +struct meta_data { + /*! @brief Node type. */ + using node_type = internal::meta_data_node; + + /*! @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; + } + + /*! @copydoc meta_ctor::parent */ + [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; + + /** + * @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->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->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. Otherwise, invoking the setter results in an undefined + * behavior.
+ * The type of the value must be 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 + bool set(meta_handle instance, Type &&value) const { + return node->set && node->set(std::move(instance), std::forward(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. Otherwise, invoking the getter results in an undefined behavior. + * + * @param instance An opaque instance of the underlying type. + * @return A meta any containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(meta_handle instance) const { + return node->get(std::move(instance)); + } + + /*! @copydoc meta_ctor::prop */ + [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { + return node->prop; + } + + /** + * @brief Returns the property associated with a given key. + * @param key The key to use to search for a property. + * @return The property associated with the given key, if any. + */ + [[nodiscard]] meta_prop prop(meta_any key) const { + return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return curr->id == 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); + } + +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; + } + + /*! @copydoc meta_ctor::parent */ + [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; + + /** + * @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->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->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(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.
+ * It must be possible to cast the instance to the parent type of the member + * function. Otherwise, invoking the underlying function results in an + * undefined behavior. + * + * @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 meta any 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 meta any containing the new instance, if any. + */ + template + meta_any invoke(meta_handle instance, Args &&... args) const { + meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; + return invoke(std::move(instance), arguments, sizeof...(Args)); + } + + /*! @copydoc meta_ctor::prop */ + [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { + return node->prop; + } + + /** + * @brief Returns the property associated with a given key. + * @param key The key to use to search for a property. + * @return The property associated with the given key, if any. + */ + [[nodiscard]] meta_prop prop(meta_any key) const { + return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return curr->id == 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); + } + +private: + const node_type *node; +}; + + +/*! @brief Opaque wrapper for types. */ +class meta_type { + static bool can_cast_or_convert(const internal::meta_type_node *type, const type_info info) ENTT_NOEXCEPT { + if(type->info == info) { + return true; + } + + for(const auto *curr = type->conv; curr; curr = curr->next) { + if(curr->type()->info == info) { + return true; + } + } + + for(const auto *curr = type->base; curr; curr = curr->next) { + if(auto *target = curr->type(); can_cast_or_convert(target, info)) { + return true; + } + } + + return false; + } + + template + [[nodiscard]] static const internal::meta_ctor_node * ctor(const internal::meta_ctor_node *curr, std::index_sequence) { + for(; curr; curr = curr->next) { + if(curr->arity == sizeof...(Args) && (can_cast_or_convert(internal::meta_info::resolve(), curr->arg(Index).info()) && ...)) { + return curr; + } + } + + return nullptr; + } + +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(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(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]] 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 void or not. + * @return True if the underlying type is void, false otherwise. + */ + [[nodiscard]] bool is_void() const ENTT_NOEXCEPT { + return node->is_void; + } + + /** + * @brief Checks whether a type refers to an integral type or not. + * @return True if the underlying type is an integral type, false otherwise. + */ + [[nodiscard]] bool is_integral() const ENTT_NOEXCEPT { + return node->is_integral; + } + + /** + * @brief Checks whether a type refers to a floating-point type or not. + * @return True if the underlying type is a floating-point type, false + * otherwise. + */ + [[nodiscard]] bool is_floating_point() const ENTT_NOEXCEPT { + return node->is_floating_point; + } + + /** + * @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->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->is_enum; + } + + /** + * @brief Checks whether a type refers to an union or not. + * @return True if the underlying type is an union, false otherwise. + */ + [[nodiscard]] bool is_union() const ENTT_NOEXCEPT { + return node->is_union; + } + + /** + * @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->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->is_pointer; + } + + /** + * @brief Checks whether a type refers to a function pointer or not. + * @return True if the underlying type is a function pointer, false + * otherwise. + */ + [[nodiscard]] bool is_function_pointer() const ENTT_NOEXCEPT { + return node->is_function_pointer; + } + + /** + * @brief Checks whether a type refers to a pointer to data member or not. + * @return True if the underlying type is a pointer to data member, false + * otherwise. + */ + [[nodiscard]] bool is_member_object_pointer() const ENTT_NOEXCEPT { + return node->is_member_object_pointer; + } + + /** + * @brief Checks whether a type refers to a pointer to member function or + * not. + * @return True if the underlying type is a pointer to member function, + * false otherwise. + */ + [[nodiscard]] bool is_member_function_pointer() const ENTT_NOEXCEPT { + return node->is_member_function_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->is_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->is_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->is_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->template_info.is_template_specialization; + } + + /** + * @brief Returns the number of template arguments, if any. + * @return The number of template arguments, if any. + */ + [[nodiscard]] size_type template_arity() const ENTT_NOEXCEPT { + return node->template_info.arity; + } + + /** + * @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 is_template_specialization() ? node->template_info.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(size_type index) const ENTT_NOEXCEPT { + return index < template_arity() ? node->template_info.arg(index) : meta_type{}; + } + + /** + * @brief Provides the number of dimensions of an array type. + * @return The number of dimensions in case of array types, 0 otherwise. + */ + [[nodiscard]] size_type rank() const ENTT_NOEXCEPT { + return node->rank; + } + + /** + * @brief The number of elements along the given dimension of an array type. + * @param dim The dimension of which to return the number of elements. + * @return The number of elements along the given dimension in case of array + * types, 0 otherwise. + */ + [[nodiscard]] size_type extent(size_type dim = {}) const ENTT_NOEXCEPT { + return node->extent(dim); + } + + /** + * @brief Provides the type for which the pointer is defined. + * @return The type for which the pointer is defined or this type if it + * doesn't refer to a pointer type. + */ + [[nodiscard]] meta_type remove_pointer() const ENTT_NOEXCEPT { + return node->remove_pointer(); + } + + /** + * @brief Provides the type for which the array is defined. + * @return The type for which the array is defined or this type if it + * doesn't refer to an array type. + */ + [[nodiscard]] meta_type remove_extent() const ENTT_NOEXCEPT { + return node->remove_extent(); + } + + /** + * @brief Returns a range to use to visit top-level base meta types. + * @return An iterable range to use to visit top-level base meta types. + */ + [[nodiscard]] meta_range base() const ENTT_NOEXCEPT { + return node->base; + } + + /** + * @brief Returns the base meta type associated with a given identifier. + * @param id Unique identifier. + * @return The base meta type associated with the given identifier, if any. + */ + [[nodiscard]] meta_type base(const id_type id) const { + return internal::meta_visit<&node_type::base>([id](const auto *curr) { return curr->type()->id == id; }, node); + } + + /** + * @brief Returns a range to use to visit top-level constructors. + * @return An iterable range to use to visit top-level constructors. + */ + [[nodiscard]] meta_range ctor() const ENTT_NOEXCEPT { + return node->ctor; + } + + /** + * @brief Returns a constructor for a given list of types of arguments. + * @tparam Args Constructor arguments. + * @return The requested constructor, if any. + */ + template + [[nodiscard]] meta_ctor ctor() const { + return ctor(node->ctor, std::make_index_sequence{}); + } + + /** + * @brief Returns a range to use to visit top-level data. + * @return An iterable range to use to visit top-level data. + */ + [[nodiscard]] meta_range data() const ENTT_NOEXCEPT { + return node->data; + } + + /** + * @brief Returns the data associated with a given identifier. + * + * The data of the base classes will also be visited, if any. + * + * @param id Unique identifier. + * @return The data associated with the given identifier, if any. + */ + [[nodiscard]] meta_data data(const id_type id) const { + return internal::meta_visit<&node_type::data>([id](const auto *curr) { return curr->id == id; }, node); + } + + /** + * @brief Returns a range to use to visit top-level functions. + * @return An iterable range to use to visit top-level functions. + */ + [[nodiscard]] meta_range func() const ENTT_NOEXCEPT { + return node->func; + } + + /** + * @brief Returns the function associated with a given identifier. + * + * The functions of the base classes will also be visited, if any.
+ * In the case of overloaded functions, the first one with the required + * identifier will be returned. + * + * @param id Unique identifier. + * @return The function associated with the given identifier, if any. + */ + [[nodiscard]] meta_func func(const id_type id) const { + return internal::meta_visit<&node_type::func>([id](const auto *curr) { return curr->id == id; }, node); + } + + /** + * @brief Creates an instance of the underlying type, if possible. + * + * Parameters must be such that a cast or conversion to the required types + * is possible. Otherwise, an empty and thus invalid wrapper is returned. + * + * @param args Parameters to use to construct the instance. + * @param sz Number of parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ + [[nodiscard]] meta_any construct(meta_any * const args, const size_type sz) const { + meta_any ret{}; + internal::meta_visit<&node_type::ctor>([args, sz, &ret](const auto *curr) { return (curr->arity == sz) && (ret = curr->invoke(args)); }, node); + return ret; + } + + /** + * @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 meta any containing the new instance, if any. + */ + template + [[nodiscard]] meta_any construct(Args &&... args) const { + meta_any arguments[sizeof...(Args) + 1u]{std::forward(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. Otherwise, invoking the underlying function results in an + * undefined behavior. + * + * @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 meta any 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 internal::meta_func_node* candidate{}; + size_type extent{sz + 1u}; + bool ambiguous{}; + + for(auto *it = internal::meta_visit<&node_type::func>([id, sz](const auto *curr) { return curr->id == id && curr->arity == sz; }, node); it && it->id == id && it->arity == sz; it = it->next) { + size_type direct{}; + size_type ext{}; + + for(size_type next{}; next < sz && next == (direct + ext); ++next) { + const auto type = args[next].type(); + const auto req = it->arg(next).info(); + type.info() == req ? ++direct : (ext += can_cast_or_convert(type.node, req)); + } + + if((direct + ext) == sz) { + if(ext < extent) { + candidate = it; + extent = ext; + ambiguous = false; + } else if(ext == extent) { + ambiguous = true; + } + } + } + + return (candidate && !ambiguous) ? 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 meta any containing the new instance, if any. + */ + template + meta_any invoke(const id_type id, meta_handle instance, Args &&... args) const { + meta_any arguments[sizeof...(Args) + 1u]{std::forward(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. Otherwise, invoking the setter results in an undefined + * behavior.
+ * The type of the value must be 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 + bool set(const id_type id, meta_handle instance, Type &&value) const { + auto const candidate = data(id); + return candidate ? candidate.set(std::move(instance), std::forward(value)) : false; + } + + /** + * @brief Gets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the data + * member. Otherwise, invoking the getter results in an undefined behavior. + * + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @return A meta any containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { + auto const candidate = data(id); + return candidate ? candidate.get(std::move(instance)) : meta_any{}; + } + + /** + * @brief Returns a range to use to visit top-level properties. + * @return An iterable range to use to visit top-level properties. + */ + [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { + return node->prop; + } + + /** + * @brief Returns the property associated with a given key. + * + * Properties of the base classes will also be visited, if any. + * + * @param key The key to use to search for a property. + * @return The property associated with the given key, if any. + */ + [[nodiscard]] meta_prop prop(meta_any key) const { + return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return curr->id == 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); + } + + /** + * @brief Resets a type and all its parts. + * + * This function resets a type and all its data members, member functions + * and properties, as well as its constructors, destructors and conversion + * functions if any.
+ * 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. + */ + void reset() ENTT_NOEXCEPT { + auto** it = internal::meta_context::global(); + + while(*it && *it != node) { + it = &(*it)->next; + } + + if(*it) { + *it = (*it)->next; + } + + const auto unregister_all = y_combinator{ + [](auto &&self, auto **curr, auto... member) { + while(*curr) { + auto *prev = *curr; + (self(&(prev->*member)), ...); + *curr = prev->next; + prev->next = nullptr; + } + } + }; + + unregister_all(&node->prop); + unregister_all(&node->base); + unregister_all(&node->conv); + unregister_all(&node->ctor, &internal::meta_ctor_node::prop); + unregister_all(&node->data, &internal::meta_data_node::prop); + unregister_all(&node->func, &internal::meta_func_node::prop); + + node->id = {}; + node->ctor = node->def_ctor; + node->dtor = nullptr; + } + +private: + 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 +meta_any meta_any::invoke(const id_type id, Args &&... args) const { + return type().invoke(id, *this, std::forward(args)...); +} + + +template +meta_any meta_any::invoke(const id_type id, Args &&... args) { + return type().invoke(id, *this, std::forward(args)...); +} + + +template +bool meta_any::set(const id_type id, Type &&value) { + return type().set(id, *this, std::forward(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_type meta_ctor::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +[[nodiscard]] inline meta_type meta_ctor::arg(size_type index) const ENTT_NOEXCEPT { + return index < arity() ? node->arg(index) : meta_type{}; +} + + +[[nodiscard]] inline meta_type meta_data::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +[[nodiscard]] inline meta_type meta_data::type() const ENTT_NOEXCEPT { + return node->type(); +} + + +[[nodiscard]] inline meta_type meta_func::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +[[nodiscard]] inline meta_type meta_func::ret() const ENTT_NOEXCEPT { + return node->ret(); +} + + +[[nodiscard]] inline meta_type meta_func::arg(size_type index) const ENTT_NOEXCEPT { + return index < arity() ? node->arg(index) : meta_type{}; +} + + +/*! @brief Opaque iterator for sequence containers. */ +class meta_sequence_container::meta_iterator { + /*! @brief A sequence container can access the underlying iterator. */ + friend class meta_sequence_container; + + enum class operation { INCR, DEREF }; + + using vtable_type = void(const operation, const any &, void *); + + template + static void basic_vtable(const operation op, const any &from, void *to) { + switch(op) { + case operation::INCR: + ++any_cast(const_cast(from)); + break; + case operation::DEREF: + static_cast(to)->emplace::reference>(*any_cast(from)); + break; + } + } + +public: + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Type of elements returned by the iterator. */ + using value_type = meta_any; + /*! @brief Pointer type, `void` on purpose. */ + using pointer = void; + /*! @brief Reference type, it is **not** an actual reference. */ + using reference = value_type; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + meta_iterator() ENTT_NOEXCEPT = default; + + /** + * @brief Constructs a meta iterator from a given iterator. + * @tparam It Type of actual iterator with which to build the meta iterator. + * @param iter The actual iterator with which to build the meta iterator. + */ + template + meta_iterator(It iter) + : vtable{&basic_vtable}, + handle{std::move(iter)} + {} + + /*! @brief Pre-increment operator. @return This iterator. */ + meta_iterator & operator++() ENTT_NOEXCEPT { + return vtable(operation::INCR, handle, nullptr), *this; + } + + /*! @brief Post-increment operator. @return This iterator. */ + meta_iterator operator++(int) ENTT_NOEXCEPT { + meta_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Checks if two iterators refer to the same element. + * @param other The iterator with which to compare. + * @return True if the iterators refer to the same element, false otherwise. + */ + [[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { + return handle == other.handle; + } + + /** + * @brief Checks if two iterators refer to the same element. + * @param other The iterator with which to compare. + * @return False if the iterators refer to the same element, true otherwise. + */ + [[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + /** + * @brief Indirection operator. + * @return The element to which the iterator points. + */ + [[nodiscard]] reference operator*() const { + meta_any other; + vtable(operation::DEREF, handle, &other); + return other; + } + + /** + * @brief Returns false if an iterator is invalid, true otherwise. + * @return False if the iterator is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(handle); + } + +private: + vtable_type *vtable{}; + any handle{}; +}; + + +template +struct meta_sequence_container::meta_sequence_container_proxy { + using traits_type = meta_sequence_container_traits; + + [[nodiscard]] static meta_type value_type() ENTT_NOEXCEPT { + return internal::meta_info::resolve(); + } + + [[nodiscard]] static size_type size(const any &container) ENTT_NOEXCEPT { + return traits_type::size(any_cast(container)); + } + + [[nodiscard]] static bool resize(any &container, size_type sz) { + auto * const cont = any_cast(&container); + return cont ? traits_type::resize(*cont, sz) : false; + } + + [[nodiscard]] static bool clear(any &container) { + auto * const cont = any_cast(&container); + return cont ? traits_type::clear(*cont) : false; + } + + [[nodiscard]] static iterator begin(any &container) { + if(auto * const cont = any_cast(&container); cont) { + return iterator{traits_type::begin(*cont)}; + } + + return iterator{traits_type::cbegin(any_cast(container))}; + } + + [[nodiscard]] static iterator end(any &container) { + if(auto * const cont = any_cast(&container); cont) { + return iterator{traits_type::end(*cont)}; + } + + return iterator{traits_type::cend(any_cast(container))}; + } + + [[nodiscard]] static std::pair insert(any &container, iterator it, meta_any &value) { + if(auto * const cont = any_cast(&container); cont) { + // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector + if(value.allow_cast() || value.allow_cast()) { + const auto *element = value.try_cast>(); + auto ret = traits_type::insert(*cont, any_cast(it.handle), element ? *element : value.cast()); + return { iterator{std::move(ret.first)}, ret.second }; + } + } + + return {}; + } + + [[nodiscard]] static std::pair erase(any &container, iterator it) { + if(auto * const cont = any_cast(&container); cont) { + auto ret = traits_type::erase(*cont, any_cast(it.handle)); + return { iterator{std::move(ret.first)}, ret.second }; + } + + return {}; + } + + [[nodiscard]] static meta_any get(any &container, size_type pos) { + if(auto * const cont = any_cast(&container); cont) { + return meta_any{std::in_place_type, traits_type::get(*cont, pos)}; + } + + return meta_any{std::in_place_type, traits_type::cget(any_cast(container), pos)}; + } +}; + + +/** + * @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_fn(); +} + + +/** + * @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(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 clear_fn(storage); +} + + +/** + * @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 begin_fn(storage); +} + + +/** + * @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 end_fn(storage); +} + + +/** + * @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 pair consisting of an iterator to the inserted element (in case of + * success) and a bool denoting whether the insertion took place. + */ +inline std::pair meta_sequence_container::insert(iterator it, meta_any value) { + return insert_fn(storage, it, value); +} + + +/** + * @brief Removes a given element from a container. + * @param it Iterator to the element to remove. + * @return A pair consisting of an iterator following the last removed element + * (in case of success) and a bool denoting whether the insertion took place. + */ +inline std::pair meta_sequence_container::erase(iterator it) { + return erase_fn(storage, it); +} + + +/** + * @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[](size_type pos) { + return get_fn(storage, pos); +} + + +/** + * @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(storage); +} + + +/*! @brief Opaque iterator for associative containers. */ +class meta_associative_container::meta_iterator { + enum operation { INCR, DEREF }; + + using vtable_type = void(const operation, const any &, void *); + + template + static void basic_vtable(const operation op, const any &from, void *to) { + switch(op) { + case operation::INCR: + ++any_cast(const_cast(from)); + break; + case operation::DEREF: + if constexpr(KeyOnly) { + static_cast *>(to)->first = std::cref(*any_cast(from)); + } else { + *static_cast *>(to) = std::make_pair(std::cref(any_cast(from)->first), std::ref(any_cast(from)->second)); + } + break; + } + } + +public: + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Type of elements returned by the iterator. */ + using value_type = std::pair; + /*! @brief Pointer type, `void` on purpose. */ + using pointer = void; + /*! @brief Reference type, it is **not** an actual reference. */ + using reference = value_type; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + meta_iterator() ENTT_NOEXCEPT = default; + + /** + * @brief Constructs an meta iterator from a given iterator. + * @tparam KeyOnly True if the container is also key-only, false otherwise. + * @tparam It Type of actual iterator with which to build the meta iterator. + * @param iter The actual iterator with which to build the meta iterator. + */ + template + meta_iterator(std::integral_constant, It iter) + : vtable{&basic_vtable}, + handle{std::move(iter)} + {} + + /*! @brief Pre-increment operator. @return This iterator. */ + meta_iterator & operator++() ENTT_NOEXCEPT { + return vtable(operation::INCR, handle, nullptr), *this; + } + + /*! @brief Post-increment operator. @return This iterator. */ + meta_iterator operator++(int) ENTT_NOEXCEPT { + meta_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Checks if two iterators refer to the same element. + * @param other The iterator with which to compare. + * @return True if the iterators refer to the same element, false otherwise. + */ + [[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { + return handle == other.handle; + } + + /** + * @brief Checks if two iterators refer to the same element. + * @param other The iterator with which to compare. + * @return False if the iterators refer to the same element, true otherwise. + */ + [[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + /** + * @brief Indirection operator. + * @return The element to which the iterator points. + */ + [[nodiscard]] reference operator*() const { + reference other; + vtable(operation::DEREF, handle, &other); + return other; + } + + /** + * @brief Returns false if an iterator is invalid, true otherwise. + * @return False if the iterator is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(handle); + } + +private: + vtable_type *vtable{}; + any handle{}; +}; + + +template +struct meta_associative_container::meta_associative_container_proxy { + using traits_type = meta_associative_container_traits; + + [[nodiscard]] static meta_type key_type() ENTT_NOEXCEPT { + return internal::meta_info::resolve(); + } + + [[nodiscard]] static meta_type mapped_type() ENTT_NOEXCEPT { + if constexpr(is_key_only_meta_associative_container_v) { + return meta_type{}; + } else { + return internal::meta_info::resolve(); + } + } + + [[nodiscard]] static meta_type value_type() ENTT_NOEXCEPT { + return internal::meta_info::resolve(); + } + + [[nodiscard]] static size_type size(const any &container) ENTT_NOEXCEPT { + return traits_type::size(any_cast(container)); + } + + [[nodiscard]] static bool clear(any &container) { + auto * const cont = any_cast(&container); + return cont ? traits_type::clear(*cont) : false; + } + + [[nodiscard]] static iterator begin(any &container) { + if(auto * const cont = any_cast(&container); cont) { + return iterator{is_key_only_meta_associative_container{}, traits_type::begin(*cont)}; + } + + return iterator{is_key_only_meta_associative_container{}, traits_type::cbegin(any_cast(container))}; + } + + [[nodiscard]] static iterator end(any &container) { + if(auto * const cont = any_cast(&container); cont) { + return iterator{is_key_only_meta_associative_container{}, traits_type::end(*cont)}; + } + + return iterator{is_key_only_meta_associative_container{}, traits_type::cend(any_cast(container))}; + } + + [[nodiscard]] static bool insert(any &container, meta_any &key, meta_any &value) { + if(auto * const cont = any_cast(&container); cont && key.allow_cast()) { + if constexpr(is_key_only_meta_associative_container_v) { + return traits_type::insert(*cont, key.cast()); + } else { + if(value.allow_cast()) { + return traits_type::insert(*cont, key.cast(), value.cast()); + } + } + } + + return false; + } + + [[nodiscard]] static bool erase(any &container, meta_any &key) { + if(auto * const cont = any_cast(&container); cont && key.allow_cast()) { + return traits_type::erase(*cont, key.cast()); + } + + return false; + } + + [[nodiscard]] static iterator find(any &container, meta_any &key) { + if(key.allow_cast()) { + if(auto * const cont = any_cast(&container); cont) { + return iterator{is_key_only_meta_associative_container{}, traits_type::find(*cont, key.cast())}; + } + + return iterator{is_key_only_meta_associative_container{}, traits_type::cfind(any_cast(container), key.cast())}; + } + + return {}; + } +}; + + +/** + * @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_fn(); +} + + +/** + * @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_fn(); +} + + +/*! @copydoc meta_sequence_container::value_type */ +[[nodiscard]] inline meta_type meta_associative_container::value_type() const ENTT_NOEXCEPT { + return value_type_fn(); +} + + +/*! @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 begin_fn(storage); +} + + +/*! @copydoc meta_sequence_container::end */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { + return end_fn(storage); +} + + +/** + * @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(storage); +} + + +} + + +#endif + +// #include "meta/node.hpp" +#ifndef ENTT_META_NODE_HPP +#define ENTT_META_NODE_HPP + + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "type_traits.hpp" + + + +namespace entt { + + +class meta_any; +class meta_type; +struct meta_handle; + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct meta_type_node; + + +struct meta_prop_node { + meta_prop_node * next; + const meta_any &id; + meta_any &value; +}; + + +struct meta_base_node { + meta_type_node * const parent; + meta_base_node * next; + meta_type_node *(* const type)() ENTT_NOEXCEPT; + const void *(* const cast)(const void *) ENTT_NOEXCEPT; +}; + + +struct meta_conv_node { + meta_type_node * const parent; + meta_conv_node * next; + meta_type_node *(* const type)() ENTT_NOEXCEPT; + meta_any(* const conv)(const void *); +}; + + +struct meta_ctor_node { + using size_type = std::size_t; + meta_type_node * const parent; + meta_ctor_node * next; + meta_prop_node * prop; + const size_type arity; + meta_type(* const arg)(const size_type) ENTT_NOEXCEPT; + meta_any(* const invoke)(meta_any * const); +}; + + +struct meta_data_node { + id_type id; + meta_type_node * const parent; + meta_data_node * next; + meta_prop_node * prop; + const bool is_const; + const bool is_static; + meta_type_node *(* const type)() ENTT_NOEXCEPT; + bool(* const set)(meta_handle, meta_any); + meta_any(* const get)(meta_handle); +}; + + +struct meta_func_node { + using size_type = std::size_t; + id_type id; + meta_type_node * const parent; + meta_func_node * next; + meta_prop_node * prop; + const size_type arity; + const bool is_const; + const bool is_static; + meta_type_node *(* const ret)() ENTT_NOEXCEPT; + meta_type(* const arg)(const size_type) ENTT_NOEXCEPT; + meta_any(* const invoke)(meta_handle, meta_any *); +}; + + +struct meta_template_info { + using size_type = std::size_t; + const bool is_template_specialization; + const size_type arity; + meta_type_node *(* const type)() ENTT_NOEXCEPT; + meta_type_node *(* const arg)(const size_type) ENTT_NOEXCEPT; +}; + + +struct meta_type_node { + using size_type = std::size_t; + const type_info info; + id_type id; + meta_type_node * next; + meta_prop_node * prop; + const size_type size_of; + const bool is_void; + const bool is_integral; + const bool is_floating_point; + const bool is_array; + const bool is_enum; + const bool is_union; + const bool is_class; + const bool is_pointer; + const bool is_function_pointer; + const bool is_member_object_pointer; + const bool is_member_function_pointer; + const bool is_pointer_like; + const bool is_sequence_container; + const bool is_associative_container; + const meta_template_info template_info; + const size_type rank; + size_type(* const extent)(const size_type) ENTT_NOEXCEPT ; + meta_type_node *(* const remove_pointer)() ENTT_NOEXCEPT; + meta_type_node *(* const remove_extent)() ENTT_NOEXCEPT; + meta_ctor_node * const def_ctor; + meta_ctor_node *ctor{nullptr}; + meta_base_node *base{nullptr}; + meta_conv_node *conv{nullptr}; + meta_data_node *data{nullptr}; + meta_func_node *func{nullptr}; + void(* dtor)(void *){nullptr}; +}; + + +template +auto meta_visit(const Op &op, const Node *node) +-> std::decay_t*Member)> { + for(auto *curr = node->*Member; curr; curr = curr->next) { + if(op(curr)) { + return curr; + } + } + + if constexpr(std::is_same_v) { + for(auto *curr = node->base; curr; curr = curr->next) { + if(auto *ret = meta_visit(op, curr->type()); ret) { + return ret; + } + } + } + + return nullptr; +} + + +template +meta_type_node * meta_arg_node(type_list, const std::size_t index) ENTT_NOEXCEPT; + + +template +class ENTT_API meta_node { + static_assert(std::is_same_v>>, "Invalid type"); + + template + [[nodiscard]] static auto extent(const meta_type_node::size_type dim, std::index_sequence) ENTT_NOEXCEPT { + meta_type_node::size_type ext{}; + ((ext = (dim == Index ? std::extent_v : ext)), ...); + return ext; + } + + [[nodiscard]] static meta_ctor_node * meta_default_constructor([[maybe_unused]] meta_type_node *type) ENTT_NOEXCEPT { + if constexpr(std::is_default_constructible_v) { + static meta_ctor_node node{ + type, + nullptr, + nullptr, + 0u, + nullptr, + [](meta_any * const) { return meta_any{std::in_place_type}; } + }; + + return &node; + } else { + return nullptr; + } + } + + [[nodiscard]] static meta_template_info meta_template_descriptor() ENTT_NOEXCEPT { + if constexpr(is_complete_v>) { + return { + true, + meta_template_traits::args_type::size, + &meta_node::class_type>::resolve, + [](const std::size_t index) ENTT_NOEXCEPT { + return meta_arg_node(typename meta_template_traits::args_type{}, index); + } + }; + } else { + return { false, 0u, nullptr, nullptr }; + } + } + +public: + [[nodiscard]] static meta_type_node * resolve() ENTT_NOEXCEPT { + static meta_type_node node{ + type_id(), + {}, + nullptr, + nullptr, + size_of_v, + std::is_void_v, + std::is_integral_v, + std::is_floating_point_v, + std::is_array_v, + std::is_enum_v, + std::is_union_v, + std::is_class_v, + std::is_pointer_v, + std::is_pointer_v && std::is_function_v>, + std::is_member_object_pointer_v, + std::is_member_function_pointer_v, + is_meta_pointer_like_v, + is_complete_v>, + is_complete_v>, + meta_template_descriptor(), + std::rank_v, + [](meta_type_node::size_type dim) ENTT_NOEXCEPT { return extent(dim, std::make_index_sequence>{}); }, + &meta_node>>>::resolve, + &meta_node>>>::resolve, + meta_default_constructor(&node), + meta_default_constructor(&node) + }; + + return &node; + } +}; + + +template +struct meta_info: meta_node>> {}; + + +template +meta_type_node * meta_arg_node(type_list, const std::size_t index) ENTT_NOEXCEPT { + meta_type_node *args[sizeof...(Args) + 1u]{nullptr, internal::meta_info::resolve()...}; + return args[index + 1u]; +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +} + + +#endif // #include "meta/pointer.hpp" #ifndef ENTT_META_POINTER_HPP #define ENTT_META_POINTER_HPP + #include #include // #include "type_traits.hpp" @@ -16099,6 +35198,17 @@ struct is_meta_pointer_like {}; +/** + * @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 +struct is_meta_pointer_like + : std::false_type +{}; + + /** * @brief Makes `std::shared_ptr`s of any type pointer-like types for the meta * system. @@ -16128,8 +35238,135 @@ struct is_meta_pointer_like> #endif // #include "meta/policy.hpp" +#ifndef ENTT_META_POLICY_HPP +#define ENTT_META_POLICY_HPP + + +namespace entt { + + +/*! @brief Empty class type used to request the _as ref_ policy. */ +struct as_ref_t {}; + + +/*! @brief Empty class type used to request the _as cref_ policy. */ +struct as_cref_t {}; + + +/*! @brief Empty class type used to request the _as-is_ policy. */ +struct as_is_t {}; + + +/*! @brief Empty class type used to request the _as void_ policy. */ +struct as_void_t {}; + + +} + + +#endif // #include "meta/range.hpp" +#ifndef ENTT_META_RANGE_HPP +#define ENTT_META_RANGE_HPP + + +#include +#include + + +namespace entt { + + +/** + * @brief Iterable range to use to iterate all types of meta objects. + * @tparam Type Type of meta objects returned. + * @tparam Node Type of meta nodes iterated. + */ +template +class meta_range { + struct range_iterator { + using difference_type = std::ptrdiff_t; + using value_type = Type; + using pointer = void; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using node_type = Node; + + range_iterator() ENTT_NOEXCEPT = default; + + range_iterator(node_type *head) ENTT_NOEXCEPT + : it{head} + {} + + range_iterator & operator++() ENTT_NOEXCEPT { + return (it = it->next), *this; + } + + range_iterator operator++(int) ENTT_NOEXCEPT { + range_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return it; + } + + [[nodiscard]] bool operator==(const range_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + [[nodiscard]] bool operator!=(const range_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + private: + node_type *it{}; + }; + +public: + /*! @brief Node type. */ + using node_type = Node; + /*! @brief Input iterator type. */ + using iterator = range_iterator; + + /*! @brief Default constructor. */ + meta_range() ENTT_NOEXCEPT = default; + + /** + * @brief Constructs a meta range from a given node. + * @param head The underlying node with which to construct the range. + */ + meta_range(node_type *head) + : node{head} + {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first meta object of the range. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return iterator{node}; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last meta object of the + * range. + */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return iterator{}; + } + +private: + node_type *node{nullptr}; +}; + + +} + + +#endif // #include "meta/resolve.hpp" #ifndef ENTT_META_RESOLVE_HPP @@ -16137,10 +35374,14 @@ struct is_meta_pointer_like> #include +// #include "../core/type_info.hpp" + // #include "ctx.hpp" // #include "meta.hpp" +// #include "node.hpp" + // #include "range.hpp" @@ -16173,29 +35414,467 @@ template * @param id Unique identifier. * @return The meta type associated with the given identifier, if any. */ -[[nodiscard]] inline meta_type resolve_id(const id_type id) ENTT_NOEXCEPT { - internal::meta_range range{*internal::meta_context::global()}; - return std::find_if(range.begin(), range.end(), [id](const auto &curr) { return curr.id == id; }).operator->(); +[[nodiscard]] inline meta_type resolve(const id_type id) ENTT_NOEXCEPT { + for(auto *curr = *internal::meta_context::global(); curr; curr = curr->next) { + if(curr->id == id) { + return curr; + } + } + + return {}; } /** - * @brief Returns the meta type associated with a given type id, if any. - * @param id Unique identifier. - * @return The meta type associated with the given type id, if any. + * @brief Returns the meta type associated with a given type info object, if + * any. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. */ -[[nodiscard]] inline meta_type resolve_type(const id_type id) ENTT_NOEXCEPT { - internal::meta_range range{*internal::meta_context::global()}; - return std::find_if(range.begin(), range.end(), [id](const auto &curr) { return curr.type_id == id; }).operator->(); +[[nodiscard]] inline meta_type resolve(const type_info info) ENTT_NOEXCEPT { + for(auto *curr = *internal::meta_context::global(); curr; curr = curr->next) { + if(curr->info == info) { + return curr; + } + } + + return {}; } } +#endif + +// #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 typename> +struct meta_class_template_tag {}; + + +/** + * @brief General purpose traits class for generating meta template information. + * @tparam Clazz Type of class template. + * @tparam Args Types of template arguments. + */ +template typename Clazz, typename... Args> +struct meta_template_traits> { + /*! @brief Wrapped class template. */ + using class_type = meta_class_template_tag; + /*! @brief List of template arguments. */ + using args_type = type_list; +}; + + +} + + #endif // #include "meta/type_traits.hpp" +#ifndef ENTT_META_TYPE_TRAITS_HPP +#define ENTT_META_TYPE_TRAITS_HPP + + +#include + + +namespace entt { + + +/** + * @brief Traits class template to be specialized to enable support for meta + * template information. + */ +template +struct meta_template_traits; + + +/** + * @brief Traits class template to be specialized to enable support for meta + * sequence containers. + */ +template +struct meta_sequence_container_traits; + + +/** + * @brief Traits class template to be specialized to enable support for meta + * associative containers. + */ +template +struct meta_associative_container_traits; + + +/** + * @brief Provides the member constant `value` to true if a meta associative + * container claims to wrap a key-only type, false otherwise. + * @tparam Type Potentially key-only meta associative container type. + */ +template +struct is_key_only_meta_associative_container: std::true_type {}; + + +/*! @copydoc is_key_only_meta_associative_container */ +template +struct is_key_only_meta_associative_container::type::mapped_type>> + : std::false_type +{}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially key-only meta associative container type. + */ +template +inline constexpr auto is_key_only_meta_associative_container_v = is_key_only_meta_associative_container::value; + + +/** + * @brief Provides the member constant `value` to true if a given type is a + * pointer-like type from the point of view of the meta system, false otherwise. + * @tparam Type Potentially pointer-like type. + */ +template +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 +struct is_meta_pointer_like: is_meta_pointer_like {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially pointer-like type. + */ +template +inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like::value; + + +} + + +#endif + +// #include "meta/utility.hpp" +#ifndef ENTT_META_UTILITY_HPP +#define ENTT_META_UTILITY_HPP + + +#include +#include +#include +#include +// #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 +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 +struct meta_function_descriptor { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = std::conditional_t, type_list, type_list>; + + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr auto is_const = true; + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr auto is_static = !std::is_same_v; +}; + + +/** + * @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 +struct meta_function_descriptor { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = std::conditional_t, type_list, 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 = !std::is_same_v; +}; + + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Args Function arguments. + */ +template +struct meta_function_descriptor { + /*! @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 +class meta_function_helper { + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret(Class:: *)(Args...) const); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret(Class:: *)(Args...)); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret(*)(Args...)); + +public: + /*! @brief The meta function descriptor of the given function. */ + using type = decltype(get_rid_of_noexcept(std::declval())); +}; + + +/** + * @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 +using meta_function_helper_t = typename meta_function_helper::type; + + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Args Actual types of arguments. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] static meta_type meta_arg(type_list, const std::size_t index) ENTT_NOEXCEPT { + return internal::meta_arg_node(type_list{}, index); +} + + +/** + * @brief Constructs an instance given a list of erased parameters, if possible. + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @tparam Index Indexes to use to extract erased arguments from their list. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template +[[nodiscard]] meta_any meta_construct(meta_any * const args, std::index_sequence) { + if(((args+Index)->allow_cast() && ...)) { + return Type{(args+Index)->cast()...}; + } + + return {}; +} + + +/** + * @brief Sets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to set. + * @param instance An opaque instance of the underlying type, if required. + * @param value Parameter to use to set the variable. + * @return True in case of success, false otherwise. + */ +template +[[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { + if constexpr(!std::is_same_v && !std::is_same_v) { + if constexpr(std::is_function_v>> || std::is_member_function_pointer_v) { + using descriptor = meta_function_helper_t; + using data_type = type_list_element_t, typename descriptor::args_type>; + + if(auto * const clazz = instance->try_cast(); clazz) { + if(value.allow_cast()) { + std::invoke(Data, *clazz, value.cast()); + return true; + } + } + } else if constexpr(std::is_member_object_pointer_v) { + using data_type = std::remove_reference_t().*Data)>; + + if constexpr(!std::is_array_v && !std::is_const_v) { + if(auto * const clazz = instance->try_cast(); clazz) { + if(value.allow_cast()) { + std::invoke(Data, clazz) = value.cast(); + return true; + } + } + } + } else { + using data_type = std::remove_reference_t; + + if constexpr(!std::is_array_v && !std::is_const_v) { + if(value.allow_cast()) { + *Data = value.cast(); + 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 +[[nodiscard]] meta_any meta_getter([[maybe_unused]] meta_handle instance) { + [[maybe_unused]] auto dispatch = [](auto &&value) { + if constexpr(std::is_same_v) { + return meta_any{std::in_place_type, std::forward(value)}; + } else if constexpr(std::is_same_v) { + return meta_any{std::reference_wrapper{std::forward(value)}}; + } else if constexpr(std::is_same_v) { + return meta_any{std::cref(std::forward(value))}; + } else { + static_assert(std::is_same_v, "Policy not supported"); + return meta_any{std::forward(value)}; + } + }; + + if constexpr(std::is_function_v>> || std::is_member_function_pointer_v) { + auto * const clazz = instance->try_cast, const Type, Type>>(); + return clazz ? dispatch(std::invoke(Data, *clazz)) : meta_any{}; + } else if constexpr(std::is_member_object_pointer_v) { + if constexpr(std::is_array_v().*Data)>>>) { + return meta_any{}; + } else { + if(auto * clazz = instance->try_cast(); clazz) { + return dispatch(std::invoke(Data, *clazz)); + } else { + auto * fallback = instance->try_cast(); + return fallback ? dispatch(std::invoke(Data, *fallback)) : meta_any{}; + } + } + } else if constexpr(std::is_pointer_v) { + if constexpr(std::is_array_v>) { + return meta_any{}; + } else { + return dispatch(*Data); + } + } else { + return dispatch(Data); + } +} + + +/** + * @brief Invokes a function given a list of erased parameters, if possible. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Index Indexes to use to extract erased arguments from their list. + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, meta_any *args, std::index_sequence) { + using descriptor = meta_function_helper_t; + + auto dispatch = [](auto &&... params) { + if constexpr(std::is_void_v> || std::is_same_v) { + std::invoke(Candidate, std::forward(params)...); + return meta_any{std::in_place_type}; + } else if constexpr(std::is_same_v) { + return meta_any{std::reference_wrapper{std::invoke(Candidate, std::forward(params)...)}}; + } else if constexpr(std::is_same_v) { + return meta_any{std::cref(std::invoke(Candidate, std::forward(params)...))}; + } else { + static_assert(std::is_same_v, "Policy not supported"); + return meta_any{std::invoke(Candidate, std::forward(params)...)}; + } + }; + + if constexpr(std::is_invocable_v...>) { + if(const auto * const clazz = instance->try_cast(); clazz && ((args+Index)->allow_cast>() && ...)) { + return dispatch(*clazz, (args+Index)->cast>()...); + } + } else if constexpr(std::is_invocable_v...>) { + if(auto * const clazz = instance->try_cast(); clazz && ((args+Index)->allow_cast>() && ...)) { + return dispatch(*clazz, (args+Index)->cast>()...); + } + } else { + if(((args+Index)->allow_cast>() && ...)) { + return dispatch((args+Index)->cast>()...); + } + } + + return meta_any{}; +} + + +} + + +#endif // #include "platform/android-ndk-r17.hpp" #ifndef ENTT_PLATFORM_ANDROID_NDK_R17_HPP @@ -16285,103 +35964,16 @@ using invoke_result_t = typename std::invoke_result::type; #endif -// #include "process/process.hpp" -#ifndef ENTT_PROCESS_PROCESS_HPP -#define ENTT_PROCESS_PROCESS_HPP - - -#include -#include -// #include "../config/config.h" -#ifndef ENTT_CONFIG_CONFIG_H -#define ENTT_CONFIG_CONFIG_H - - -#ifndef ENTT_NOEXCEPT -# define ENTT_NOEXCEPT noexcept -#endif - - -#ifndef ENTT_HS_SUFFIX -# define ENTT_HS_SUFFIX _hs -#endif - - -#ifndef ENTT_HWS_SUFFIX -# define ENTT_HWS_SUFFIX _hws -#endif - - -#ifndef ENTT_USE_ATOMIC -# define ENTT_MAYBE_ATOMIC(Type) Type -#else -# include -# define ENTT_MAYBE_ATOMIC(Type) std::atomic -#endif - - -#ifndef ENTT_ID_TYPE -# include -# define ENTT_ID_TYPE std::uint32_t -#endif - - -#ifndef ENTT_PAGE_SIZE -# define ENTT_PAGE_SIZE 32768 -#endif - - -#ifndef ENTT_ASSERT -# include -# define ENTT_ASSERT(condition) assert(condition) -#endif - - -#ifndef ENTT_NO_ETO -# include -# define ENTT_IS_EMPTY(Type) std::is_empty -#else -# include -# define ENTT_IS_EMPTY(Type) std::false_type -#endif - - -#ifndef ENTT_STANDARD_CPP -# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) -# define ENTT_PRETTY_FUNCTION_CONSTEXPR -# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ -# define ENTT_PRETTY_FUNCTION_PREFIX '=' -# define ENTT_PRETTY_FUNCTION_SUFFIX ']' -# elif 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_CONSTEXPR -# define ENTT_PRETTY_FUNCTION __FUNCSIG__ -# define ENTT_PRETTY_FUNCTION_PREFIX '<' -# define ENTT_PRETTY_FUNCTION_SUFFIX '>' -# endif -#endif - - -#ifndef ENTT_STANDALONE -# define ENTT_FAST_PATH(...) false -#else -# define ENTT_FAST_PATH(Cond) Cond -#endif - - -#endif - -// #include "../core/type_traits.hpp" -#ifndef ENTT_CORE_TYPE_TRAITS_HPP -#define ENTT_CORE_TYPE_TRAITS_HPP +// #include "poly/poly.hpp" +#ifndef ENTT_POLY_POLY_HPP +#define ENTT_POLY_POLY_HPP #include -#include +#include +#include #include +#include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H @@ -16392,16 +35984,6 @@ using invoke_result_t = typename std::invoke_result::type; #endif -#ifndef ENTT_HS_SUFFIX -# define ENTT_HS_SUFFIX _hs -#endif - - -#ifndef ENTT_HWS_SUFFIX -# define ENTT_HWS_SUFFIX _hws -#endif - - #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else @@ -16416,12 +35998,17 @@ using invoke_result_t = typename std::invoke_result::type; #endif -#ifndef ENTT_PAGE_SIZE -# define ENTT_PAGE_SIZE 32768 +#ifdef ENTT_PAGE_SIZE +static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); +#else +# define ENTT_PAGE_SIZE 4096 #endif -#ifndef ENTT_ASSERT +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition) assert(condition) #endif @@ -16437,17 +36024,11 @@ using invoke_result_t = typename std::invoke_result::type; #ifndef ENTT_STANDARD_CPP -# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) -# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' -# elif 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_CONSTEXPR +# elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' @@ -16455,10 +36036,78 @@ using invoke_result_t = typename std::invoke_result::type; #endif -#ifndef ENTT_STANDALONE -# define ENTT_FAST_PATH(...) false +#endif + +// #include "../core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type #else -# define ENTT_FAST_PATH(Cond) Cond +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifdef ENTT_PAGE_SIZE +static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); +#else +# define ENTT_PAGE_SIZE 4096 +#endif + + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif #endif @@ -16469,6 +36118,7 @@ using invoke_result_t = typename std::invoke_result::type; #define ENTT_CORE_FWD_HPP +#include // #include "../config/config.h" @@ -16476,20 +36126,683 @@ using invoke_result_t = typename std::invoke_result::type; namespace entt { +template)> +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; + + } #endif +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + + +#include +#include +// #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 "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + namespace entt { +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +struct fnv1a_traits; + + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string { + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *curr) ENTT_NOEXCEPT: str{curr} {} + const Char *str; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT { + auto value = traits_type::offset; + + while(*curr != 0) { + value = (value ^ static_cast(*(curr++))) * traits_type::prime; + } + + return value; + } + +public: + /*! @brief Character type. */ + using value_type = Char; + /*! @brief Unsigned integer type. */ + using hash_type = id_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { + id_type partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + + /** + * @brief Returns directly the numeric representation of a string. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * const auto value = basic_hashed_string::to_value("my.png"); + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + return helper(str); + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { + return helper(wrapper.str); + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_NOEXCEPT + : str{nullptr}, hash{} + {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * basic_hashed_string hs{"my.png"}; + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param curr Human-readable identifer. + */ + template + constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT + : str{curr}, hash{helper(curr)} + {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + : str{wrapper.str}, hash{helper(wrapper.str)} + {} + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the instance. + */ + [[nodiscard]] constexpr const value_type * data() const ENTT_NOEXCEPT { + return str; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { + return hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { return data(); } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } + + /** + * @brief Compares two hashed strings. + * @param other Hashed string with which to compare. + * @return True if the two hashed strings are identical, false otherwise. + */ + [[nodiscard]] constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { + return hash == other.hash; + } + +private: + const value_type *str; + hash_type hash; +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the character type of the hashed string directly from a + * human-readable identifer provided to the constructor. + * + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + */ +template +basic_hashed_string(const Char (&str)[N]) +-> basic_hashed_string; + + +/** + * @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 +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + + +inline namespace literals { + + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_string{str}; +} + + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_wstring{str}; +} + + +} + + +} + + +#endif + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct ENTT_API type_seq final { + [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + + +template +[[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().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + + +template +[[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()); + return value; +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_seq final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() ENTT_NOEXCEPT { + static const id_type value = internal::type_seq::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } +}; + + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +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(0); +#else + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_seq::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 +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(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } +}; + + +/*! @brief Implementation specific information about a type. */ +class type_info final { + template + friend type_info type_id() ENTT_NOEXCEPT; + + type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT + : seq_value{seq_v}, + hash_value{hash_v}, + name_value{name_v} + {} + +public: + /*! @brief Default constructor. */ + type_info() ENTT_NOEXCEPT + : type_info({}, {}, {}) + {} + + /*! @brief Default copy constructor. */ + type_info(const type_info &) ENTT_NOEXCEPT = default; + /*! @brief Default move constructor. */ + type_info(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Default copy assignment operator. + * @return This type info object. + */ + type_info & operator=(const type_info &) ENTT_NOEXCEPT = default; + + /** + * @brief Default move assignment operator. + * @return This type info object. + */ + type_info & operator=(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Checks if a type info object is properly initialized. + * @return True if the object is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return name_value.data() != nullptr; + } + + /** + * @brief Type sequential identifier. + * @return Type sequential identifier. + */ + [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { + return seq_value; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { + return hash_value; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { + return name_value; + } + + /** + * @brief Compares the contents of two type info objects. + * @param other Object with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const type_info &other) const ENTT_NOEXCEPT { + return hash_value == other.hash_value; + } + +private: + id_type seq_value; + id_type hash_value; + std::string_view name_value; +}; + + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two contents differ, false otherwise. + */ +[[nodiscard]] inline bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/** + * @brief Returns the type info object for a given type. + * @tparam Type Type for which to generate a type info object. + * @return The type info object for the given type. + */ +template +[[nodiscard]] type_info type_id() ENTT_NOEXCEPT { + return type_info{ + type_seq>>::value(), + type_hash>>::value(), + type_name>>::value() + }; +} + + +} + + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // Unfortunately, doxygen cannot parse such a construct. + /*! @cond TURN_OFF_DOXYGEN */ + : choice_t + /*! @endcond */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t 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 +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::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 +struct size_of: std::integral_constant {}; + + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr auto size_of_v = size_of::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. @@ -16517,7 +36830,7 @@ using integral_constant = std::integral_constant; /** - * @brief Alias template to ease the creation of named values. + * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template @@ -16525,57 +36838,64 @@ using tag = integral_constant; /** - * @brief Utility class to disambiguate overloaded functions. - * @tparam N Number of choices available. - */ -template -struct choice_t - // Unfortunately, doxygen cannot parse such a construct. - /*! @cond TURN_OFF_DOXYGEN */ - : choice_t - /*! @endcond */ -{}; - - -/*! @copybrief choice_t */ -template<> -struct choice_t<0> {}; - - -/** - * @brief Variable template for the choice trick. - * @tparam N Number of choices available. - */ -template -inline constexpr choice_t choice{}; - - -/*! @brief A class to use to push around lists of types, nothing more. */ -template -struct type_list {}; - - -/*! @brief Primary template isn't defined on purpose. */ -template -struct type_list_size; - - -/** - * @brief Compile-time number of elements in a type list. + * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template -struct type_list_size> - : std::integral_constant +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 +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 +struct type_list_element> + : type_list_element> {}; /** - * @brief Helper variable template. - * @tparam List Type list. + * @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 -inline constexpr auto type_list_size_v = type_list_size::value; +template +struct type_list_element<0u, type_list> { + /*! @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 +using type_list_element_t = typename type_list_element::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 +constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ @@ -16660,20 +36980,213 @@ template using type_list_unique_t = typename type_list_unique::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 +struct type_list_contains; + + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other>: std::disjunction...> {}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr auto type_list_contains_v = type_list_contains::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +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 +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 +struct value_list_element> + : value_list_element> +{}; + + +/** + * @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 +struct value_list_element<0u, value_list> { + /*! @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 +inline constexpr auto value_list_element_v = value_list_element::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 +constexpr value_list operator+(value_list, value_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +[[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) +-> decltype(std::declval() == std::declval()) { return true; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>); +} + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type Potentially equality comparable type. */ -template> -struct is_equality_comparable: std::false_type {}; - - -/*! @copydoc is_equality_comparable */ -template -struct is_equality_comparable() == std::declval())>> - : std::true_type -{}; +template +struct is_equality_comparable: std::bool_constant(choice<2>)> {}; /** @@ -16684,15 +37197,96 @@ template inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; +/*! @brief Same as std::is_invocable, but with tuples. */ +template +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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** +* @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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr auto is_applicable_v = is_applicable::value; + + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +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 +struct is_applicable_r>: std::is_invocable_r {}; + + +/** + * @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 +inline constexpr auto is_applicable_r_v = is_applicable_r::value; + + +/** +* @brief Provides the member constant `value` to true if a given type is +* complete, false otherwise. +* @tparam Type Potential complete type. +*/ +template +struct is_complete: std::false_type {}; + + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + + +/** +* @brief Helper variable template. +* @tparam Type Potential complete type. +*/ +template +inline constexpr auto is_complete_v = is_complete::value; + + /** * @brief Provides the member constant `value` to true if a given type is empty * and the empty type optimization is enabled, false otherwise. * @tparam Type Potential empty type. */ template -struct is_eto_eligible - : ENTT_IS_EMPTY(Type) -{}; +struct is_empty: ENTT_IS_EMPTY(Type) {}; /** @@ -16700,7 +37294,36 @@ struct is_eto_eligible * @tparam Type Potential empty type. */ template -inline constexpr auto is_eto_eligible_v = is_eto_eligible::value; +inline constexpr auto is_empty_v = is_empty::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 +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::add_const_t; +}; + + +/** + * @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 +using constness_as_t = typename constness_as::type; /** @@ -16741,6 +37364,1801 @@ using member_class_t = typename member_class::type; +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 +class basic_any { + enum class operation { COPY, MOVE, DTOR, COMP, ADDR, CADDR, REF, CREF, TYPE }; + + using storage_type = std::aligned_storage_t; + using vtable_type = const void *(const operation, const basic_any &, const void *); + + template + static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; + + template + [[nodiscard]] static bool compare(const void *lhs, const void *rhs) { + if constexpr(!std::is_function_v && is_equality_comparable_v) { + return *static_cast(lhs) == *static_cast(rhs); + } else { + return lhs == rhs; + } + } + + template + static Type & as(const void *to) { + return *const_cast(static_cast(to)); + } + + template + static const void * basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &from, [[maybe_unused]] const void *to) { + if constexpr(std::is_void_v) { + switch(op) { + case operation::COPY: + case operation::MOVE: + case operation::REF: + case operation::CREF: + as(to).vtable = from.vtable; + break; + default: + break; + } + } else if constexpr(std::is_lvalue_reference_v) { + using base_type = std::decay_t; + + switch(op) { + case operation::COPY: + if constexpr(std::is_copy_constructible_v) { + as(to) = *static_cast(from.instance); + } + break; + case operation::MOVE: + as(to).instance = from.instance; + as(to).vtable = from.vtable; + [[fallthrough]]; + case operation::DTOR: + break; + case operation::COMP: + return compare(from.instance, to) ? to : nullptr; + case operation::ADDR: + return std::is_const_v> ? nullptr : from.instance; + case operation::CADDR: + return from.instance; + case operation::REF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::CREF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::TYPE: + as(to) = type_id(); + break; + } + } else if constexpr(in_situ) { + #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L + auto *instance = const_cast(std::launder(reinterpret_cast(&from.storage))); + #else + auto *instance = const_cast(reinterpret_cast(&from.storage)); + #endif + + switch(op) { + case operation::COPY: + if constexpr(std::is_copy_constructible_v) { + new (&as(to).storage) Type{std::as_const(*instance)}; + as(to).vtable = from.vtable; + } + break; + case operation::MOVE: + new (&as(to).storage) Type{std::move(*instance)}; + as(to).vtable = from.vtable; + break; + case operation::DTOR: + instance->~Type(); + break; + case operation::COMP: + return compare(instance, to) ? to : nullptr; + case operation::ADDR: + case operation::CADDR: + return instance; + case operation::REF: + as(to).instance = instance; + as(to).vtable = basic_vtable; + break; + case operation::CREF: + as(to).instance = instance; + as(to).vtable = basic_vtable; + break; + case operation::TYPE: + as(to) = type_id(); + break; + } + } else { + switch(op) { + case operation::COPY: + if constexpr(std::is_copy_constructible_v) { + as(to).instance = new Type{*static_cast(from.instance)}; + as(to).vtable = from.vtable; + } + break; + case operation::MOVE: + as(to).instance = std::exchange(as(&from).instance, nullptr); + as(to).vtable = from.vtable; + break; + case operation::DTOR: + if constexpr(std::is_array_v) { + delete[] static_cast(from.instance); + } else { + delete static_cast(from.instance); + } + break; + case operation::COMP: + return compare(from.instance, to) ? to : nullptr; + case operation::ADDR: + case operation::CADDR: + return from.instance; + case operation::REF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::CREF: + as(to).instance = from.instance; + as(to).vtable = basic_vtable; + break; + case operation::TYPE: + as(to) = type_id(); + break; + } + } + + return nullptr; + } + + template + void initialize([[maybe_unused]] Args &&... args) { + if constexpr(!std::is_void_v) { + if constexpr(std::is_lvalue_reference_v) { + static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); + instance = (std::addressof(args), ...); + } else if constexpr(in_situ) { + if constexpr(std::is_aggregate_v) { + new (&storage) Type{std::forward(args)...}; + } else { + new (&storage) Type(std::forward(args)...); + } + } else { + if constexpr(std::is_aggregate_v) { + instance = new Type{std::forward(args)...}; + } else { + instance = new Type(std::forward(args)...); + } + } + } + } + +public: + /*! @brief Default constructor. */ + basic_any() ENTT_NOEXCEPT + : basic_any{std::in_place_type} + {} + + /** + * @brief Constructs an any by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&... args) + : instance{}, + vtable{&basic_vtable} + { + initialize(std::forward(args)...); + } + + /** + * @brief Constructs an any that holds an unmanaged object. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template + basic_any(std::reference_wrapper value) ENTT_NOEXCEPT + : basic_any{std::in_place_type, value.get()} + {} + + /** + * @brief Constructs an any from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, basic_any>>> + basic_any(Type &&value) + : basic_any{std::in_place_type>, std::forward(value)} + {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + basic_any(const basic_any &other) + : basic_any{std::in_place_type} + { + other.vtable(operation::COPY, other, this); + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_any(basic_any &&other) ENTT_NOEXCEPT + : basic_any{std::in_place_type} + { + other.vtable(operation::MOVE, other, this); + } + + /*! @brief Frees the internal storage, whatever it means. */ + ~basic_any() { + vtable(operation::DTOR, *this, nullptr); + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ + basic_any & operator=(const basic_any &other) { + vtable(operation::DTOR, *this, nullptr); + 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) { + vtable(operation::DTOR, *this, nullptr); + other.vtable(operation::MOVE, other, this); + 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 + basic_any & operator=(std::reference_wrapper value) ENTT_NOEXCEPT { + emplace(value.get()); + 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 + std::enable_if_t, basic_any>, basic_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns the type of the contained object. + * @return The type of the contained object, if any. + */ + [[nodiscard]] type_info type() const ENTT_NOEXCEPT { + type_info info{}; + vtable(operation::TYPE, *this, &info); + return info; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void * data() const ENTT_NOEXCEPT { + return vtable(operation::CADDR, *this, nullptr); + } + + /*! @copydoc data */ + [[nodiscard]] void * data() ENTT_NOEXCEPT { + return const_cast(vtable(operation::ADDR, *this, nullptr)); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + void emplace(Args &&... args) { + std::exchange(vtable, &basic_vtable)(operation::DTOR, *this, nullptr); + initialize(std::forward(args)...); + } + + /*! @brief Destroys contained object */ + void reset() { + std::exchange(vtable, &basic_vtable)(operation::DTOR, *this, nullptr); + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(vtable(operation::CADDR, *this, nullptr) == nullptr); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ + bool operator==(const basic_any &other) const ENTT_NOEXCEPT { + return type() == other.type() && (vtable(operation::COMP, *this, other.data()) == other.data()); + } + + /** + * @brief Aliasing constructor. + * @return An any that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT { + basic_any ref{}; + vtable(operation::REF, *this, &ref); + return ref; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT { + basic_any ref{}; + vtable(operation::CREF, *this, &ref); + return ref; + } + +private: + union { const void *instance; storage_type storage; }; + vtable_type *vtable; +}; + + +/** + * @brief Checks if two wrappers differ in their content. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param lhs A wrapper, either empty or not. + * @param rhs A wrapper, either empty or not. + * @return True if the two wrappers differ in their content, false otherwise. + */ +template +[[nodiscard]] inline bool operator!=(const basic_any &lhs, const basic_any &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 +Type any_cast(const basic_any &data) ENTT_NOEXCEPT { + const auto * const instance = any_cast>(&data); + ENTT_ASSERT(instance); + return static_cast(*instance); +} + + +/*! @copydoc any_cast */ +template +Type any_cast(basic_any &data) ENTT_NOEXCEPT { + // forces const on non-reference types to make them work also with wrappers for const references + auto * const instance = any_cast>(&data); + ENTT_ASSERT(instance); + return static_cast(*instance); +} + + +/*! @copydoc any_cast */ +template +Type any_cast(basic_any &&data) ENTT_NOEXCEPT { + // forces const on non-reference types to make them work also with wrappers for const references + auto * const instance = any_cast>(&data); + ENTT_ASSERT(instance); + return static_cast(std::move(*instance)); +} + + +/*! @copydoc any_cast */ +template +const Type * any_cast(const basic_any *data) ENTT_NOEXCEPT { + return (data->type() == type_id() ? static_cast(data->data()) : nullptr); +} + + +/*! @copydoc any_cast */ +template +Type * any_cast(basic_any *data) ENTT_NOEXCEPT { + // last attempt to make wrappers for const references return their values + return (data->type() == type_id() ? static_cast(static_cast, Type> *>(data)->data()) : nullptr); +} + + +} + + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "hashed_string.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct ENTT_API type_seq final { + [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + + +template +[[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().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + + +template +[[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()); + return value; +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_seq final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() ENTT_NOEXCEPT { + static const id_type value = internal::type_seq::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } +}; + + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +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(0); +#else + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_seq::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 +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(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } +}; + + +/*! @brief Implementation specific information about a type. */ +class type_info final { + template + friend type_info type_id() ENTT_NOEXCEPT; + + type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT + : seq_value{seq_v}, + hash_value{hash_v}, + name_value{name_v} + {} + +public: + /*! @brief Default constructor. */ + type_info() ENTT_NOEXCEPT + : type_info({}, {}, {}) + {} + + /*! @brief Default copy constructor. */ + type_info(const type_info &) ENTT_NOEXCEPT = default; + /*! @brief Default move constructor. */ + type_info(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Default copy assignment operator. + * @return This type info object. + */ + type_info & operator=(const type_info &) ENTT_NOEXCEPT = default; + + /** + * @brief Default move assignment operator. + * @return This type info object. + */ + type_info & operator=(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Checks if a type info object is properly initialized. + * @return True if the object is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return name_value.data() != nullptr; + } + + /** + * @brief Type sequential identifier. + * @return Type sequential identifier. + */ + [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { + return seq_value; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { + return hash_value; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { + return name_value; + } + + /** + * @brief Compares the contents of two type info objects. + * @param other Object with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const type_info &other) const ENTT_NOEXCEPT { + return hash_value == other.hash_value; + } + +private: + id_type seq_value; + id_type hash_value; + std::string_view name_value; +}; + + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two contents differ, false otherwise. + */ +[[nodiscard]] inline bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/** + * @brief Returns the type info object for a given type. + * @tparam Type Type for which to generate a type info object. + * @return The type info object for the given type. + */ +template +[[nodiscard]] type_info type_id() ENTT_NOEXCEPT { + return type_info{ + type_seq>>::value(), + type_hash>>::value(), + type_name>>::value() + }; +} + + +} + + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // Unfortunately, doxygen cannot parse such a construct. + /*! @cond TURN_OFF_DOXYGEN */ + : choice_t + /*! @endcond */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t 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 +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::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 +struct size_of: std::integral_constant {}; + + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr auto size_of_v = size_of::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 +using unpack_as_t = Type; + + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_v = Value; + + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +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 +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 +struct type_list_element> + : type_list_element> +{}; + + +/** + * @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 +struct type_list_element<0u, type_list> { + /*! @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 +using type_list_element_t = typename type_list_element::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 +constexpr type_list operator+(type_list, type_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + std::disjunction_v...>, + typename type_list_unique>::type, + type_list_cat_t, typename type_list_unique>::type> + >; +}; + + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = type_list<>; +}; + + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::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 +struct type_list_contains; + + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other>: std::disjunction...> {}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr auto type_list_contains_v = type_list_contains::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +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 +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 +struct value_list_element> + : value_list_element> +{}; + + +/** + * @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 +struct value_list_element<0u, value_list> { + /*! @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 +inline constexpr auto value_list_element_v = value_list_element::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 +constexpr value_list operator+(value_list, value_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +[[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) +-> decltype(std::declval() == std::declval()) { return true; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>); +} + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template +struct is_equality_comparable: std::bool_constant(choice<2>)> {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** +* @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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr auto is_applicable_v = is_applicable::value; + + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +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 +struct is_applicable_r>: std::is_invocable_r {}; + + +/** + * @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 +inline constexpr auto is_applicable_r_v = is_applicable_r::value; + + +/** +* @brief Provides the member constant `value` to true if a given type is +* complete, false otherwise. +* @tparam Type Potential complete type. +*/ +template +struct is_complete: std::false_type {}; + + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + + +/** +* @brief Helper variable template. +* @tparam Type Potential complete type. +*/ +template +inline constexpr auto is_complete_v = is_complete::value; + + +/** + * @brief Provides the member constant `value` to true if a given type is empty + * and the empty type optimization is enabled, false otherwise. + * @tparam Type Potential empty type. + */ +template +struct is_empty: ENTT_IS_EMPTY(Type) {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potential empty type. + */ +template +inline constexpr auto is_empty_v = is_empty::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 +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::add_const_t; +}; + + +/** + * @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 +using constness_as_t = typename constness_as::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 +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class * clazz(Ret(Class:: *)(Args...)); + + template + static Class * clazz(Ret(Class:: *)(Args...) const); + + template + static Class * clazz(Type Class:: *); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + + +} + + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_POLY_FWD_HPP +#define ENTT_POLY_FWD_HPP + + +#include + + +namespace entt { + + +template)> +class basic_poly; + + +/** + * @brief Alias declaration for the most common use case. + * @tparam Concept Concept descriptor. + */ +template +using poly = basic_poly; + + +} + + +#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 + 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 + poly_inspector invoke(Args &&... args) const; + + /*! @copydoc invoke */ + template + 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 +class poly_vtable { + using inspector = typename Concept::template type; + + template + static auto vtable_entry(Ret(*)(inspector &, Args...)) -> Ret(*)(basic_any &, Args...); + + template + static auto vtable_entry(Ret(*)(const inspector &, Args...)) -> Ret(*)(const basic_any &, Args...); + + template + static auto vtable_entry(Ret(*)(Args...)) -> Ret(*)(const basic_any &, Args...); + + template + static auto vtable_entry(Ret(inspector:: *)(Args...)) -> Ret(*)(basic_any &, Args...); + + template + static auto vtable_entry(Ret(inspector:: *)(Args...) const) -> Ret(*)(const basic_any &, Args...); + + template + static auto make_vtable(value_list) + -> decltype(std::make_tuple(vtable_entry(Candidate)...)); + + template + [[nodiscard]] static constexpr auto make_vtable(type_list) { + if constexpr(sizeof...(Func) == 0) { + return decltype(make_vtable(typename Concept::template impl{})){}; + } else if constexpr((std::is_function_v && ...)) { + return decltype(std::make_tuple(vtable_entry(std::declval())...)){}; + } + } + + template + static void fill_vtable_entry(Ret(* &entry)(Any &, Args...)) { + if constexpr(std::is_invocable_r_v) { + entry = +[](Any &, Args... args) -> Ret { + return std::invoke(Candidate, std::forward(args)...); + }; + } else { + entry = +[](Any &instance, Args... args) -> Ret { + return static_cast(std::invoke(Candidate, any_cast &>(instance), std::forward(args)...)); + }; + } + } + + template + [[nodiscard]] static auto fill_vtable(std::index_sequence) { + type impl{}; + (fill_vtable_entry>>(std::get(impl)), ...); + return impl; + } + +public: + /*! @brief Virtual table type. */ + using type = decltype(make_vtable(Concept{})); + + /** + * @brief Returns a static virtual table for a specific concept and type. + * @tparam Type The type for which to generate the virtual table. + * @return A static virtual table for the given concept and type. + */ + template + [[nodiscard]] static const auto * instance() { + static_assert(std::is_same_v>, "Type differs from its decayed form"); + static const auto vtable = fill_vtable(std::make_index_sequence::size>{}); + return &vtable; + } +}; + + +/** + * @brief Poly base class used to inject functionalities into concepts. + * @tparam Poly The outermost poly class. + */ +template +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 + [[nodiscard]] decltype(auto) invoke(const poly_base &self, Args &&... args) const { + const auto &poly = static_cast(self); + return std::get(*poly.vtable)(poly.storage, std::forward(args)...); + } + + /*! @copydoc invoke */ + template + [[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&... args) { + auto &poly = static_cast(self); + return std::get(*poly.vtable)(poly.storage, std::forward(args)...); + } +}; + + +/** + * @brief Shortcut for calling `poly_base::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 +decltype(auto) poly_call(Poly &&self, Args &&... args) { + return std::forward(self).template invoke(self, std::forward(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.
+ * This class aims to make it simple and easy to use. + * + * @note + * Both deduced and defined static virtual tables are supported.
+ * 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 +class basic_poly: private Concept::template type>> { + /*! @brief A poly base is allowed to snoop into a poly object. */ + friend struct poly_base; + + using vtable_type = typename poly_vtable::type; + +public: + /*! @brief Concept type. */ + using concept_type = typename Concept::template 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 + explicit basic_poly(std::in_place_type_t, Args &&... args) + : storage{std::in_place_type, std::forward(args)...}, + vtable{poly_vtable::template instance>>()} + {} + + /** + * @brief Constructs a poly that holds an unmanaged object. + * @tparam Type Type of object to use to initialize the poly. + * @param value An instance of an object to use to initialize the poly. + */ + template + basic_poly(std::reference_wrapper value) + : basic_poly{std::in_place_type, value.get()} + {} + + /** + * @brief Constructs a poly from a given value. + * @tparam Type Type of object to use to initialize the poly. + * @param value An instance of an object to use to initialize the poly. + */ + template>, basic_poly>>> + basic_poly(Type &&value) ENTT_NOEXCEPT + : basic_poly{std::in_place_type>>, std::forward(value)} + {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + basic_poly(const basic_poly &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_poly(basic_poly &&other) ENTT_NOEXCEPT + : basic_poly{} + { + swap(*this, other); + } + + /** + * @brief Assignment operator. + * @param other The instance to assign from. + * @return This poly object. + */ + basic_poly & operator=(basic_poly other) { + swap(other, *this); + return *this; + } + + /** + * @brief Returns the type of the contained object. + * @return The type of the contained object, if any. + */ + [[nodiscard]] type_info type() const ENTT_NOEXCEPT { + return storage.type(); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void * data() const ENTT_NOEXCEPT { + return storage.data(); + } + + /*! @copydoc data */ + [[nodiscard]] void * data() ENTT_NOEXCEPT { + return storage.data(); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the poly. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + void emplace(Args &&... args) { + *this = basic_poly{std::in_place_type, std::forward(args)...}; + } + + /*! @brief Destroys contained object */ + void reset() { + *this = basic_poly{}; + } + + /** + * @brief Returns false if a poly is empty, true otherwise. + * @return False if the poly is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(vtable == nullptr); + } + + /** + * @brief Returns a pointer to the underlying concept. + * @return A pointer to the underlying concept. + */ + [[nodiscard]] concept_type * operator->() ENTT_NOEXCEPT { + return this; + } + + /*! @copydoc operator-> */ + [[nodiscard]] const concept_type * operator->() const ENTT_NOEXCEPT { + return this; + } + + /** + * @brief Swaps two poly objects. + * @param lhs A valid poly object. + * @param rhs A valid poly object. + */ + friend void swap(basic_poly &lhs, basic_poly &rhs) { + using std::swap; + swap(lhs.storage, rhs.storage); + swap(lhs.vtable, rhs.vtable); + } + + /** + * @brief Aliasing constructor. + * @return A poly that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_poly as_ref() ENTT_NOEXCEPT { + basic_poly ref = std::as_const(*this).as_ref(); + ref.storage = storage.as_ref(); + return ref; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_poly as_ref() const ENTT_NOEXCEPT { + basic_poly ref{}; + ref.storage = storage.as_ref(); + ref.vtable = vtable; + return ref; + } + +private: + basic_any storage; + const vtable_type *vtable; +}; + + +} + + +#endif + +// #include "process/process.hpp" +#ifndef ENTT_PROCESS_PROCESS_HPP +#define ENTT_PROCESS_PROCESS_HPP + + +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifdef ENTT_PAGE_SIZE +static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); +#else +# define ENTT_PAGE_SIZE 4096 +#endif + + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + + +#endif + + + namespace entt { @@ -16812,35 +39230,36 @@ class process { SUCCEEDED, FAILED, ABORTED, - FINISHED + FINISHED, + REJECTED }; template - auto next(integral_constant) + auto next(std::integral_constant) -> decltype(std::declval().init(), void()) { static_cast(this)->init(); } template - auto next(integral_constant, Delta delta, void *data) + auto next(std::integral_constant, Delta delta, void *data) -> decltype(std::declval().update(delta, data), void()) { static_cast(this)->update(delta, data); } template - auto next(integral_constant) + auto next(std::integral_constant) -> decltype(std::declval().succeeded(), void()) { static_cast(this)->succeeded(); } template - auto next(integral_constant) + auto next(std::integral_constant) -> decltype(std::declval().failed(), void()) { static_cast(this)->failed(); } template - auto next(integral_constant) + auto next(std::integral_constant) -> decltype(std::declval().aborted(), void()) { static_cast(this)->aborted(); } @@ -16952,7 +39371,7 @@ public: * @return True if the process terminated with errors, false otherwise. */ [[nodiscard]] bool rejected() const ENTT_NOEXCEPT { - return stopped; + return current == state::REJECTED; } /** @@ -16963,11 +39382,11 @@ public: void tick(const Delta delta, void *data = nullptr) { switch (current) { case state::UNINITIALIZED: - next(integral_constant{}); + next(std::integral_constant{}); current = state::RUNNING; break; case state::RUNNING: - next(integral_constant{}, delta, data); + next(std::integral_constant{}, delta, data); break; default: // suppress warnings @@ -16977,18 +39396,16 @@ public: // if it's dead, it must be notified and removed immediately switch(current) { case state::SUCCEEDED: - next(integral_constant{}); + next(std::integral_constant{}); current = state::FINISHED; break; case state::FAILED: - next(integral_constant{}); - current = state::FINISHED; - stopped = true; + next(std::integral_constant{}); + current = state::REJECTED; break; case state::ABORTED: - next(integral_constant{}); - current = state::FINISHED; - stopped = true; + next(std::integral_constant{}); + current = state::REJECTED; break; default: // suppress warnings @@ -16998,7 +39415,6 @@ public: private: state current{state::UNINITIALIZED}; - bool stopped{false}; }; @@ -17082,6 +39498,341 @@ struct process_adaptor: process, Delta>, private Fu // #include "../config/config.h" // #include "process.hpp" +#ifndef ENTT_PROCESS_PROCESS_HPP +#define ENTT_PROCESS_PROCESS_HPP + + +#include +#include +// #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.
+ * 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 +class process { + enum class state: unsigned int { + UNINITIALIZED = 0, + RUNNING, + PAUSED, + SUCCEEDED, + FAILED, + ABORTED, + FINISHED, + REJECTED + }; + + template + auto next(std::integral_constant) + -> decltype(std::declval().init(), void()) { + static_cast(this)->init(); + } + + template + auto next(std::integral_constant, Delta delta, void *data) + -> decltype(std::declval().update(delta, data), void()) { + static_cast(this)->update(delta, data); + } + + template + auto next(std::integral_constant) + -> decltype(std::declval().succeeded(), void()) { + static_cast(this)->succeeded(); + } + + template + auto next(std::integral_constant) + -> decltype(std::declval().failed(), void()) { + static_cast(this)->failed(); + } + + template + auto next(std::integral_constant) + -> decltype(std::declval().aborted(), void()) { + static_cast(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() { + static_assert(std::is_base_of_v, "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 dead() const ENTT_NOEXCEPT { + return current == state::FINISHED; + } + + /** + * @brief Returns true if a process is currently paused. + * @return True if the process is paused, false otherwise. + */ + [[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{}); + current = state::RUNNING; + break; + case state::RUNNING: + next(std::integral_constant{}, 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{}); + current = state::FINISHED; + break; + case state::FAILED: + next(std::integral_constant{}); + current = state::REJECTED; + break; + case state::ABORTED: + next(std::integral_constant{}); + 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.
+ * 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 +struct process_adaptor: process, 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 + process_adaptor(Args &&... args) + : Func{std::forward(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(); }); + } +}; + + +} + + +#endif @@ -17158,19 +39909,19 @@ class scheduler { auto *process = static_cast(handler.instance.get()); process->tick(delta, data); - auto dead = process->dead(); - - if(dead) { - if(handler.next && !process->rejected()) { + if(process->rejected()) { + return true; + } else if(process->dead()) { + if(handler.next) { handler = std::move(*handler.next); // forces the process to exit the uninitialized state - dead = handler.update(handler, {}, nullptr); - } else { - handler.instance.reset(); + return handler.update(handler, {}, nullptr); } + + return true; } - return dead; + return false; } template @@ -17326,19 +40077,17 @@ public: * @param data Optional data. */ void update(const Delta delta, void *data = nullptr) { - bool clean = false; + auto sz = handlers.size(); for(auto pos = handlers.size(); pos; --pos) { auto &handler = handlers[pos-1]; - const bool dead = handler.update(handler, delta, data); - clean = clean || dead; + + if(const auto dead = handler.update(handler, delta, data); dead) { + std::swap(handler, handlers[--sz]); + } } - if(clean) { - handlers.erase(std::remove_if(handlers.begin(), handlers.end(), [](auto &handler) { - return !handler.instance; - }), handlers.end()); - } + handlers.erase(handlers.begin() + sz, handlers.end()); } /** @@ -17378,7 +40127,6 @@ private: #define ENTT_RESOURCE_CACHE_HPP -#include #include #include #include @@ -17392,16 +40140,6 @@ private: #endif -#ifndef ENTT_HS_SUFFIX -# define ENTT_HS_SUFFIX _hs -#endif - - -#ifndef ENTT_HWS_SUFFIX -# define ENTT_HWS_SUFFIX _hws -#endif - - #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else @@ -17416,12 +40154,17 @@ private: #endif -#ifndef ENTT_PAGE_SIZE -# define ENTT_PAGE_SIZE 32768 +#ifdef ENTT_PAGE_SIZE +static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); +#else +# define ENTT_PAGE_SIZE 4096 #endif -#ifndef ENTT_ASSERT +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition) assert(condition) #endif @@ -17437,17 +40180,11 @@ private: #ifndef ENTT_STANDARD_CPP -# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) -# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' -# elif 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_CONSTEXPR +# elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' @@ -17455,13 +40192,6 @@ private: #endif -#ifndef ENTT_STANDALONE -# define ENTT_FAST_PATH(...) false -#else -# define ENTT_FAST_PATH(Cond) Cond -#endif - - #endif // #include "../core/fwd.hpp" @@ -17469,6 +40199,7 @@ private: #define ENTT_CORE_FWD_HPP +#include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H @@ -17479,16 +40210,6 @@ private: #endif -#ifndef ENTT_HS_SUFFIX -# define ENTT_HS_SUFFIX _hs -#endif - - -#ifndef ENTT_HWS_SUFFIX -# define ENTT_HWS_SUFFIX _hws -#endif - - #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else @@ -17503,12 +40224,17 @@ private: #endif -#ifndef ENTT_PAGE_SIZE -# define ENTT_PAGE_SIZE 32768 +#ifdef ENTT_PAGE_SIZE +static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); +#else +# define ENTT_PAGE_SIZE 4096 #endif -#ifndef ENTT_ASSERT +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition) assert(condition) #endif @@ -17524,17 +40250,11 @@ private: #ifndef ENTT_STANDARD_CPP -# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) -# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' -# elif 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_CONSTEXPR +# elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' @@ -17542,13 +40262,6 @@ private: #endif -#ifndef ENTT_STANDALONE -# define ENTT_FAST_PATH(...) false -#else -# define ENTT_FAST_PATH(Cond) Cond -#endif - - #endif @@ -17556,10 +40269,18 @@ private: namespace entt { +template)> +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; + + } @@ -17587,7 +40308,7 @@ struct resource_cache; template -class resource_handle; +struct resource_handle; template @@ -17617,25 +40338,23 @@ namespace entt { * @tparam Resource Type of resource managed by a handle. */ template -class resource_handle { - /*! @brief Resource handles are friends of their caches. */ - friend struct resource_cache; +struct resource_handle { + /*! @brief Default constructor. */ + resource_handle() ENTT_NOEXCEPT = default; + /** + * @brief Creates a handle from a shared pointer, namely a resource. + * @param res A pointer to a properly initialized resource. + */ resource_handle(std::shared_ptr res) ENTT_NOEXCEPT : resource{std::move(res)} {} -public: - /*! @brief Default constructor. */ - resource_handle() ENTT_NOEXCEPT = default; - /** * @brief Gets a reference to the managed resource. * * @warning - * The behavior is undefined if the handle doesn't contain a resource.
- * An assertion will abort the execution at runtime in debug mode if the - * handle is empty. + * The behavior is undefined if the handle doesn't contain a resource. * * @return A reference to the managed resource. */ @@ -17673,9 +40392,7 @@ public: * @brief Gets a pointer to the managed resource. * * @warning - * The behavior is undefined if the handle doesn't contain a resource.
- * An assertion will abort the execution at runtime in debug mode if the - * handle is empty. + * The behavior is undefined if the handle doesn't contain a resource. * * @return A pointer to the managed resource or `nullptr` if the handle * contains no resource at all. @@ -17713,9 +40430,10 @@ private: #define ENTT_RESOURCE_LOADER_HPP -#include // #include "fwd.hpp" +// #include "handle.hpp" + namespace entt { @@ -17727,14 +40445,14 @@ namespace entt { * Resource loaders must inherit from this class and stay true to the CRTP * idiom. Moreover, a resource loader must expose a public, const member * function named `load` that accepts a variable number of arguments and returns - * a shared pointer to the resource just created.
+ * a handle to the resource just created.
* As an example: * * @code{.cpp} * struct my_resource {}; * * struct my_loader: entt::resource_loader { - * std::shared_ptr load(int) const { + * resource_handle load(int value) const { * // use the integer value somehow * return std::make_shared(); * } @@ -17765,7 +40483,7 @@ class resource_loader { * @return The resource just loaded or an empty pointer in case of errors. */ template - [[nodiscard]] std::shared_ptr get(Args &&... args) const { + [[nodiscard]] resource_handle get(Args &&... args) const { return static_cast(this)->load(std::forward(args)...); } }; @@ -17860,18 +40578,16 @@ struct resource_cache { template resource_handle load(const id_type id, Args &&... args) { static_assert(std::is_base_of_v, Loader>, "Invalid loader type"); - resource_handle resource{}; if(auto it = resources.find(id); it == resources.cend()) { - if(auto instance = Loader{}.get(std::forward(args)...); instance) { - resources[id] = instance; - resource = std::move(instance); + if(auto handle = temp(std::forward(args)...); handle) { + return (resources[id] = std::move(handle)); } } else { - resource = it->second; + return it->second; } - return resource; + return {}; } /** @@ -17916,7 +40632,7 @@ struct resource_cache { */ template [[nodiscard]] resource_handle temp(Args &&... args) const { - return { Loader{}.get(std::forward(args)...) }; + return Loader{}.get(std::forward(args)...); } /** @@ -17933,8 +40649,11 @@ struct resource_cache { * @return A handle for the given resource. */ [[nodiscard]] resource_handle handle(const id_type id) const { - auto it = resources.find(id); - return { it == resources.end() ? nullptr : it->second }; + if(auto it = resources.find(id); it != resources.cend()) { + return it->second; + } + + return {}; } /** @@ -17988,15 +40707,15 @@ struct resource_cache { if constexpr(std::is_invocable_v) { func(curr->first); } else if constexpr(std::is_invocable_v>) { - func(resource_handle{ curr->second }); + func(curr->second); } else { - func(curr->first, resource_handle{ curr->second }); + func(curr->first, curr->second); } } } private: - std::unordered_map> resources; + std::unordered_map> resources; }; @@ -18006,8 +40725,189 @@ private: #endif // #include "resource/handle.hpp" +#ifndef ENTT_RESOURCE_HANDLE_HPP +#define ENTT_RESOURCE_HANDLE_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Shared resource handle. + * + * A shared resource handle is a small class that wraps a resource and keeps it + * alive even if it's deleted from the cache. It can be either copied or + * moved. A handle shares a reference to the same resource with all the other + * handles constructed for the same identifier.
+ * As a rule of thumb, resources should never be copied nor moved. Handles are + * the way to go to keep references to them. + * + * @tparam Resource Type of resource managed by a handle. + */ +template +struct resource_handle { + /*! @brief Default constructor. */ + resource_handle() ENTT_NOEXCEPT = default; + + /** + * @brief Creates a handle from a shared pointer, namely a resource. + * @param res A pointer to a properly initialized resource. + */ + resource_handle(std::shared_ptr res) ENTT_NOEXCEPT + : resource{std::move(res)} + {} + + /** + * @brief Gets 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]] const Resource & get() const ENTT_NOEXCEPT { + ENTT_ASSERT(static_cast(resource)); + return *resource; + } + + /*! @copydoc get */ + [[nodiscard]] Resource & get() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).get()); + } + + /*! @copydoc get */ + [[nodiscard]] operator const Resource & () const ENTT_NOEXCEPT { + return get(); + } + + /*! @copydoc get */ + [[nodiscard]] operator Resource & () ENTT_NOEXCEPT { + return get(); + } + + /*! @copydoc get */ + [[nodiscard]] const Resource & operator *() const ENTT_NOEXCEPT { + return get(); + } + + /*! @copydoc get */ + [[nodiscard]] Resource & operator *() ENTT_NOEXCEPT { + return get(); + } + + /** + * @brief Gets a pointer to the managed resource. + * + * @warning + * The behavior is undefined if the handle doesn't contain a resource. + * + * @return A pointer to the managed resource or `nullptr` if the handle + * contains no resource at all. + */ + [[nodiscard]] const Resource * operator->() const ENTT_NOEXCEPT { + ENTT_ASSERT(static_cast(resource)); + return resource.get(); + } + + /*! @copydoc operator-> */ + [[nodiscard]] Resource * operator->() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).operator->()); + } + + /** + * @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(resource); + } + +private: + std::shared_ptr resource; +}; + + +} + + +#endif // #include "resource/loader.hpp" +#ifndef ENTT_RESOURCE_LOADER_HPP +#define ENTT_RESOURCE_LOADER_HPP + + +// #include "fwd.hpp" + +// #include "handle.hpp" + + + +namespace entt { + + +/** + * @brief Base class for resource loaders. + * + * Resource loaders must inherit from this class and stay true to the CRTP + * idiom. Moreover, a resource loader must expose a public, const member + * function named `load` that accepts a variable number of arguments and returns + * a handle to the resource just created.
+ * As an example: + * + * @code{.cpp} + * struct my_resource {}; + * + * struct my_loader: entt::resource_loader { + * resource_handle load(int value) const { + * // use the integer value somehow + * return std::make_shared(); + * } + * }; + * @endcode + * + * In general, resource loaders should not have a state or retain data of any + * type. They should let the cache manage their resources instead. + * + * @note + * Base class and CRTP idiom aren't strictly required with the current + * implementation. One could argue that a cache can easily work with loaders of + * any type. However, future changes won't be breaking ones by forcing the use + * of a base class today and that's why the model is already in its place. + * + * @tparam Loader Type of the derived class. + * @tparam Resource Type of resource for which to use the loader. + */ +template +class resource_loader { + /*! @brief Resource loaders are friends of their caches. */ + friend struct resource_cache; + + /** + * @brief Loads the resource and returns it. + * @tparam Args Types of arguments for the loader. + * @param args Arguments for the loader. + * @return The resource just loaded or an empty pointer in case of errors. + */ + template + [[nodiscard]] resource_handle get(Args &&... args) const { + return static_cast(this)->load(std::forward(args)...); + } +}; + + +} + + +#endif // #include "signal/delegate.hpp" #ifndef ENTT_SIGNAL_DELEGATE_HPP @@ -18019,6 +40919,14 @@ private: #include #include #include +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + + +#include +#include +#include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H @@ -18029,16 +40937,6 @@ private: #endif -#ifndef ENTT_HS_SUFFIX -# define ENTT_HS_SUFFIX _hs -#endif - - -#ifndef ENTT_HWS_SUFFIX -# define ENTT_HWS_SUFFIX _hws -#endif - - #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else @@ -18053,12 +40951,17 @@ private: #endif -#ifndef ENTT_PAGE_SIZE -# define ENTT_PAGE_SIZE 32768 +#ifdef ENTT_PAGE_SIZE +static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); +#else +# define ENTT_PAGE_SIZE 4096 #endif -#ifndef ENTT_ASSERT +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition) assert(condition) #endif @@ -18074,17 +40977,11 @@ private: #ifndef ENTT_STANDARD_CPP -# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) -# define ENTT_PRETTY_FUNCTION_CONSTEXPR +# if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' -# elif 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_CONSTEXPR +# elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' @@ -18092,10 +40989,734 @@ private: #endif -#ifndef ENTT_STANDALONE -# define ENTT_FAST_PATH(...) false +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +#include +// #include "../config/config.h" + + + +namespace entt { + + +template)> +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; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // Unfortunately, doxygen cannot parse such a construct. + /*! @cond TURN_OFF_DOXYGEN */ + : choice_t + /*! @endcond */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t 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 +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::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 +struct size_of: std::integral_constant {}; + + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr auto size_of_v = size_of::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 +using unpack_as_t = Type; + + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_v = Value; + + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +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 +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 +struct type_list_element> + : type_list_element> +{}; + + +/** + * @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 +struct type_list_element<0u, type_list> { + /*! @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 +using type_list_element_t = typename type_list_element::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 +constexpr type_list operator+(type_list, type_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + std::disjunction_v...>, + typename type_list_unique>::type, + type_list_cat_t, typename type_list_unique>::type> + >; +}; + + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = type_list<>; +}; + + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::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 +struct type_list_contains; + + +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other>: std::disjunction...> {}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr auto type_list_contains_v = type_list_contains::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +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 +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 +struct value_list_element> + : value_list_element> +{}; + + +/** + * @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 +struct value_list_element<0u, value_list> { + /*! @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 +inline constexpr auto value_list_element_v = value_list_element::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 +constexpr value_list operator+(value_list, value_list) { return {}; } + + +/*! @brief Primary template isn't defined on purpose. */ +template +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 +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; + + +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +[[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) +-> decltype(std::declval() == std::declval()) { return true; } + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>); +} + + +template +[[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) +-> decltype(std::declval(), std::declval() == std::declval()) { + return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template +struct is_equality_comparable: std::bool_constant(choice<2>)> {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** +* @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 class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; + + +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr auto is_applicable_v = is_applicable::value; + + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +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 +struct is_applicable_r>: std::is_invocable_r {}; + + +/** + * @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 +inline constexpr auto is_applicable_r_v = is_applicable_r::value; + + +/** +* @brief Provides the member constant `value` to true if a given type is +* complete, false otherwise. +* @tparam Type Potential complete type. +*/ +template +struct is_complete: std::false_type {}; + + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + + +/** +* @brief Helper variable template. +* @tparam Type Potential complete type. +*/ +template +inline constexpr auto is_complete_v = is_complete::value; + + +/** + * @brief Provides the member constant `value` to true if a given type is empty + * and the empty type optimization is enabled, false otherwise. + * @tparam Type Potential empty type. + */ +template +struct is_empty: ENTT_IS_EMPTY(Type) {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potential empty type. + */ +template +inline constexpr auto is_empty_v = is_empty::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 +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::add_const_t; +}; + + +/** + * @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 +using constness_as_t = typename constness_as::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 +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class * clazz(Ret(Class:: *)(Args...)); + + template + static Class * clazz(Ret(Class:: *)(Args...) const); + + template + static Class * clazz(Type Class:: *); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + + +} + + +#endif + +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type #else -# define ENTT_FAST_PATH(Cond) Cond +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifdef ENTT_PAGE_SIZE +static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two"); +#else +# define ENTT_PAGE_SIZE 4096 +#endif + + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty +#else +# include +# define ENTT_IS_EMPTY(Type) std::false_type +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif #endif @@ -18192,7 +41813,7 @@ class delegate { [[nodiscard]] auto wrap(std::index_sequence) ENTT_NOEXCEPT { return [](const void *, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); - return Ret(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); }; } @@ -18200,8 +41821,8 @@ class delegate { [[nodiscard]] auto wrap(Type &, std::index_sequence) ENTT_NOEXCEPT { return [](const void *payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); - Type *curr = static_cast(const_cast, const void *, void *>>(payload)); - return Ret(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + Type *curr = static_cast(const_cast *>(payload)); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); }; } @@ -18209,8 +41830,8 @@ class delegate { [[nodiscard]] auto wrap(Type *, std::index_sequence) ENTT_NOEXCEPT { return [](const void *payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); - Type *curr = static_cast(const_cast, const void *, void *>>(payload)); - return Ret(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + Type *curr = static_cast(const_cast *>(payload)); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); }; } @@ -18272,7 +41893,7 @@ public: return Ret(std::invoke(Candidate, std::forward(args)...)); }; } else if constexpr(std::is_member_pointer_v) { - fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); + fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); } else { fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); } @@ -18299,7 +41920,7 @@ public: if constexpr(std::is_invocable_r_v) { fn = [](const void *payload, Args... args) -> Ret { - Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + Type *curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); }; } else { @@ -18323,7 +41944,7 @@ public: if constexpr(std::is_invocable_r_v) { fn = [](const void *payload, Args... args) -> Ret { - Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + Type *curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, curr, std::forward(args)...)); }; } else { @@ -18374,15 +41995,13 @@ public: * * @warning * Attempting to trigger an invalid delegate results in undefined - * behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * delegate has not yet been set. + * 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(fn); + ENTT_ASSERT(static_cast(*this)); return fn(data, std::forward(args)...); } @@ -18429,7 +42048,7 @@ template * @tparam Candidate Function or member to connect to the delegate. */ template -delegate(connect_arg_t) ENTT_NOEXCEPT +delegate(connect_arg_t) -> delegate>>; @@ -18439,13 +42058,17 @@ delegate(connect_arg_t) ENTT_NOEXCEPT * @tparam Type Type of class or type of payload. */ template -delegate(connect_arg_t, Type &&) ENTT_NOEXCEPT +delegate(connect_arg_t, Type &&) -> delegate>>; -/*! @brief Deduction guide. */ +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ template -delegate(Ret(*)(const void *, Args...), const void * = nullptr) ENTT_NOEXCEPT +delegate(Ret(*)(const void *, Args...), const void * = nullptr) -> delegate; @@ -18471,97 +42094,26 @@ delegate(Ret(*)(const void *, Args...), const void * = nullptr) ENTT_NOEXCEPT #define ENTT_CORE_FWD_HPP +#include // #include "../config/config.h" -#ifndef ENTT_CONFIG_CONFIG_H -#define ENTT_CONFIG_CONFIG_H - - -#ifndef ENTT_NOEXCEPT -# define ENTT_NOEXCEPT noexcept -#endif - - -#ifndef ENTT_HS_SUFFIX -# define ENTT_HS_SUFFIX _hs -#endif - - -#ifndef ENTT_HWS_SUFFIX -# define ENTT_HWS_SUFFIX _hws -#endif - - -#ifndef ENTT_USE_ATOMIC -# define ENTT_MAYBE_ATOMIC(Type) Type -#else -# include -# define ENTT_MAYBE_ATOMIC(Type) std::atomic -#endif - - -#ifndef ENTT_ID_TYPE -# include -# define ENTT_ID_TYPE std::uint32_t -#endif - - -#ifndef ENTT_PAGE_SIZE -# define ENTT_PAGE_SIZE 32768 -#endif - - -#ifndef ENTT_ASSERT -# include -# define ENTT_ASSERT(condition) assert(condition) -#endif - - -#ifndef ENTT_NO_ETO -# include -# define ENTT_IS_EMPTY(Type) std::is_empty -#else -# include -# define ENTT_IS_EMPTY(Type) std::false_type -#endif - - -#ifndef ENTT_STANDARD_CPP -# if defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) -# define ENTT_PRETTY_FUNCTION_CONSTEXPR -# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ -# define ENTT_PRETTY_FUNCTION_PREFIX '=' -# define ENTT_PRETTY_FUNCTION_SUFFIX ']' -# elif 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_CONSTEXPR -# define ENTT_PRETTY_FUNCTION __FUNCSIG__ -# define ENTT_PRETTY_FUNCTION_PREFIX '<' -# define ENTT_PRETTY_FUNCTION_SUFFIX '>' -# endif -#endif - - -#ifndef ENTT_STANDALONE -# define ENTT_FAST_PATH(...) false -#else -# define ENTT_FAST_PATH(Cond) Cond -#endif - - -#endif namespace entt { +template)> +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; + + } @@ -18573,6 +42125,7 @@ using id_type = ENTT_ID_TYPE; #include +#include // #include "../config/config.h" // #include "../core/attribute.h" @@ -18702,6 +42255,18 @@ public: /*! @brief Unsigned integer type. */ using hash_type = id_type; + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { + id_type partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + /** * @brief Returns directly the numeric representation of a string. * @@ -18731,18 +42296,6 @@ public: return helper(wrapper.str); } - /** - * @brief Returns directly the numeric representation of a string view. - * @param str Human-readable identifer. - * @param size Length of the string to hash. - * @return The numeric representation of the string. - */ - [[nodiscard]] static hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { - id_type partial{traits_type::offset}; - while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } - return partial; - } - /*! @brief Constructs an empty hashed string. */ constexpr basic_hashed_string() ENTT_NOEXCEPT : str{nullptr}, hash{} @@ -18827,7 +42380,7 @@ private: * @param str Human-readable identifer. */ template -basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; @@ -18852,7 +42405,7 @@ using hashed_string = basic_hashed_string; using hashed_wstring = basic_hashed_string; -} +inline namespace literals { /** @@ -18860,7 +42413,7 @@ using hashed_wstring = basic_hashed_string; * @param str The literal without its suffix. * @return A properly initialized hashed string. */ -[[nodiscard]] constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT { +[[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_string{str}; } @@ -18870,11 +42423,17 @@ using hashed_wstring = basic_hashed_string; * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ -[[nodiscard]] constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { +[[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_wstring{str}; } +} + + +} + + #endif // #include "fwd.hpp" @@ -18893,7 +42452,7 @@ namespace entt { namespace internal { -struct ENTT_API type_index { +struct ENTT_API type_seq final { [[nodiscard]] static id_type next() ENTT_NOEXCEPT { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; @@ -18902,18 +42461,49 @@ struct ENTT_API type_index { template -[[nodiscard]] constexpr auto type_name() ENTT_NOEXCEPT { +[[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{}; + return std::string_view{""}; #endif } +template().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + + +template +[[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()); + return value; +} + + } @@ -18924,92 +42514,176 @@ template /** - * @brief Type index. + * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template -struct ENTT_API type_index { +struct ENTT_API type_seq final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() ENTT_NOEXCEPT { - static const id_type value = internal::type_index::next(); + static const id_type value = internal::type_seq::next(); return value; } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** - * @brief Provides the member constant `value` to true if a given type is - * indexable, false otherwise. - * @tparam Type Potentially indexable type. - */ -template -struct has_type_index: std::false_type {}; - - -/*! @brief has_type_index */ -template -struct has_type_index::value())>>: std::true_type {}; - - -/** - * @brief Helper variable template. - * @tparam Type Potentially indexable type. - */ -template -inline constexpr bool has_type_index_v = has_type_index::value; - - -/** - * @brief Type info. - * @tparam Type Type for which to generate information. + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. */ template -struct type_info { +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_CONSTEXPR - [[nodiscard]] static constexpr id_type id() ENTT_NOEXCEPT { - constexpr auto value = hashed_string::value(ENTT_PRETTY_FUNCTION); - return value; - } -#elif defined ENTT_PRETTY_FUNCTION - [[nodiscard]] static id_type id() ENTT_NOEXCEPT { - static const auto value = hashed_string::value(ENTT_PRETTY_FUNCTION); - return value; - } +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return internal::type_hash(0); #else - [[nodiscard]] static id_type id() ENTT_NOEXCEPT { - return type_index::value(); - } + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_seq::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 +struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ -#if defined ENTT_PRETTY_FUNCTION_CONSTEXPR - [[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT { - constexpr auto value = internal::type_name(); - return value; + [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { + return internal::type_name(0); } -#elif defined ENTT_PRETTY_FUNCTION - [[nodiscard]] static std::string_view name() ENTT_NOEXCEPT { - static const auto value = internal::type_name(); - return value; - } -#else - [[nodiscard]] static constexpr std::string_view name() ENTT_NOEXCEPT { - return internal::type_name(); - } -#endif + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } }; +/*! @brief Implementation specific information about a type. */ +class type_info final { + template + friend type_info type_id() ENTT_NOEXCEPT; + + type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT + : seq_value{seq_v}, + hash_value{hash_v}, + name_value{name_v} + {} + +public: + /*! @brief Default constructor. */ + type_info() ENTT_NOEXCEPT + : type_info({}, {}, {}) + {} + + /*! @brief Default copy constructor. */ + type_info(const type_info &) ENTT_NOEXCEPT = default; + /*! @brief Default move constructor. */ + type_info(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Default copy assignment operator. + * @return This type info object. + */ + type_info & operator=(const type_info &) ENTT_NOEXCEPT = default; + + /** + * @brief Default move assignment operator. + * @return This type info object. + */ + type_info & operator=(type_info &&) ENTT_NOEXCEPT = default; + + /** + * @brief Checks if a type info object is properly initialized. + * @return True if the object is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return name_value.data() != nullptr; + } + + /** + * @brief Type sequential identifier. + * @return Type sequential identifier. + */ + [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { + return seq_value; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { + return hash_value; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { + return name_value; + } + + /** + * @brief Compares the contents of two type info objects. + * @param other Object with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const type_info &other) const ENTT_NOEXCEPT { + return hash_value == other.hash_value; + } + +private: + id_type seq_value; + id_type hash_value; + std::string_view name_value; +}; + + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two contents differ, false otherwise. + */ +[[nodiscard]] inline bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/** + * @brief Returns the type info object for a given type. + * @tparam Type Type for which to generate a type info object. + * @return The type info object for the given type. + */ +template +[[nodiscard]] type_info type_id() ENTT_NOEXCEPT { + return type_info{ + type_seq>>::value(), + type_hash>>::value(), + type_name>>::value() + }; +} + + } @@ -19029,6 +42703,373 @@ struct type_info { // #include "../config/config.h" // #include "delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + + +#include +#include +#include +#include +#include +// #include "../core/type_traits.hpp" + +// #include "../config/config.h" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +auto function_pointer(Ret(*)(Args...)) -> Ret(*)(Args...); + + +template +auto function_pointer(Ret(*)(Type, Args...), Other &&) -> Ret(*)(Args...); + + +template +auto function_pointer(Ret(Class:: *)(Args...), Other &&...) -> Ret(*)(Args...); + + +template +auto function_pointer(Ret(Class:: *)(Args...) const, Other &&...) -> Ret(*)(Args...); + + +template +auto function_pointer(Type Class:: *, Other &&...) -> Type(*)(); + + +template +using function_pointer_t = decltype(internal::function_pointer(std::declval()...)); + + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret(*)(Args...)) { + return std::index_sequence_for{}; +} + + +} + + +/** + * Internal details not to be documented. + * @endcond + */ + + +/*! @brief Used to wrap a function or a member of a specified type. */ +template +struct connect_arg_t {}; + + +/*! @brief Constant of type connect_arg_t used to disambiguate calls. */ +template +inline constexpr connect_arg_t 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 +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 +class delegate { + template + [[nodiscard]] auto wrap(std::index_sequence) ENTT_NOEXCEPT { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(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 + : fn{nullptr}, data{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 + delegate(connect_arg_t) ENTT_NOEXCEPT { + connect(); + } + + /** + * @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 + delegate(connect_arg_t, Type &&value_or_instance) ENTT_NOEXCEPT { + connect(std::forward(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 + void connect() ENTT_NOEXCEPT { + data = nullptr; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *, Args... args) -> Ret { + return Ret(std::invoke(Candidate, std::forward(args)...)); + }; + } else if constexpr(std::is_member_pointer_v) { + fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); + } else { + fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @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.
+ * 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 + void connect(Type &value_or_instance) ENTT_NOEXCEPT { + data = &value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(payload)); + return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @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 + void connect(Type *value_or_instance) ENTT_NOEXCEPT { + data = value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(payload)); + return Ret(std::invoke(Candidate, curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @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.
+ * 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 { + fn = function; + data = payload; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() ENTT_NOEXCEPT { + fn = nullptr; + data = 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 * instance() const ENTT_NOEXCEPT { + return data; + } + + /** + * @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(*this)); + return fn(data, std::forward(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 test also data + 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 &other) const ENTT_NOEXCEPT { + return fn == other.fn && data == other.data; + } + +private: + function_type *fn; + const void *data; +}; + + +/** + * @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 +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) +-> delegate>>; + + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + */ +template +delegate(connect_arg_t, Type &&) +-> delegate>>; + + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +delegate(Ret(*)(const void *, Args...), const void * = nullptr) +-> delegate; + + +} + + +#endif // #include "fwd.hpp" #ifndef ENTT_SIGNAL_FWD_HPP @@ -19314,7 +43355,7 @@ private: * 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. @@ -19567,7 +43608,8 @@ private: * @tparam Args Types of arguments of a function type. */ template -sink(sigh &) ENTT_NOEXCEPT -> sink; +sink(sigh &) +-> sink; } @@ -19598,11 +43640,12 @@ class dispatcher { virtual void publish() = 0; virtual void disconnect(void *) = 0; virtual void clear() ENTT_NOEXCEPT = 0; - [[nodiscard]] virtual id_type type_id() const ENTT_NOEXCEPT = 0; }; template struct pool_handler final: basic_pool { + static_assert(std::is_same_v>, "Invalid event type"); + using signal_type = sigh; using sink_type = typename signal_type::sink_type; @@ -19643,10 +43686,6 @@ class dispatcher { } } - [[nodiscard]] id_type type_id() const ENTT_NOEXCEPT override { - return type_info::id(); - } - private: signal_type signal{}; std::vector events; @@ -19654,27 +43693,29 @@ class dispatcher { template [[nodiscard]] pool_handler & assure() { - static_assert(std::is_same_v>, "Invalid event type"); + const auto index = type_seq::value(); - if constexpr(ENTT_FAST_PATH(has_type_index_v)) { - const auto index = type_index::value(); - - if(!(index < pools.size())) { - pools.resize(index+1u); - } - - if(!pools[index]) { - pools[index].reset(new pool_handler{}); - } - - return static_cast &>(*pools[index]); - } else { - auto it = std::find_if(pools.begin(), pools.end(), [id = type_info::id()](const auto &cpool) { return id == cpool->type_id(); }); - return static_cast &>(it == pools.cend() ? *pools.emplace_back(new pool_handler{}) : **it); + if(!(index < pools.size())) { + pools.resize(std::size_t(index)+1u); } + + if(!pools[index]) { + pools[index].reset(new pool_handler{}); + } + + return static_cast &>(*pools[index]); } public: + /*! @brief Default constructor. */ + dispatcher() = default; + + /*! @brief Default move constructor. */ + dispatcher(dispatcher &&) = default; + + /*! @brief Default move assignment operator. @return This dispatcher. */ + dispatcher & operator=(dispatcher &&) = default; + /** * @brief Returns a sink object for the given event. * @@ -19881,7 +43922,7 @@ namespace entt { * required to specify in advance the full list of accepted types.
* 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 + * 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. @@ -19892,11 +43933,12 @@ class emitter { virtual ~basic_pool() = default; virtual bool empty() const ENTT_NOEXCEPT = 0; virtual void clear() ENTT_NOEXCEPT = 0; - virtual id_type type_id() const ENTT_NOEXCEPT = 0; }; template struct pool_handler final: basic_pool { + static_assert(std::is_same_v>, "Invalid event type"); + using listener_type = std::function; using element_type = std::pair; using container_type = std::list; @@ -19961,10 +44003,6 @@ class emitter { on_list.remove_if([](auto &&element) { return element.first; }); } - [[nodiscard]] id_type type_id() const ENTT_NOEXCEPT override { - return type_info::id(); - } - private: bool publishing{false}; container_type once_list{}; @@ -19972,30 +44010,24 @@ class emitter { }; template - [[nodiscard]] const pool_handler & assure() const { - static_assert(std::is_same_v>, "Invalid event type"); + [[nodiscard]] pool_handler * assure() { + const auto index = type_seq::value(); - if constexpr(ENTT_FAST_PATH(has_type_index_v)) { - const auto index = type_index::value(); - - if(!(index < pools.size())) { - pools.resize(index+1u); - } - - if(!pools[index]) { - pools[index].reset(new pool_handler{}); - } - - return static_cast &>(*pools[index]); - } else { - auto it = std::find_if(pools.begin(), pools.end(), [id = type_info::id()](const auto &cpool) { return id == cpool->type_id(); }); - return static_cast &>(it == pools.cend() ? *pools.emplace_back(new pool_handler{}) : **it); + if(!(index < pools.size())) { + pools.resize(std::size_t(index)+1u); } + + if(!pools[index]) { + pools[index].reset(new pool_handler{}); + } + + return static_cast *>(pools[index].get()); } template - [[nodiscard]] pool_handler & assure() { - return const_cast &>(std::as_const(*this).template assure()); + [[nodiscard]] const pool_handler * assure() const { + const auto index = type_seq::value(); + return (!(index < pools.size()) || !pools[index]) ? nullptr : static_cast *>(pools[index].get()); } public: @@ -20057,7 +44089,7 @@ public: template void publish(Args &&... args) { Event instance{std::forward(args)...}; - assure().publish(instance, *static_cast(this)); + assure()->publish(instance, *static_cast(this)); } /** @@ -20082,7 +44114,7 @@ public: */ template connection on(listener instance) { - return assure().on(std::move(instance)); + return assure()->on(std::move(instance)); } /** @@ -20107,7 +44139,7 @@ public: */ template connection once(listener instance) { - return assure().once(std::move(instance)); + return assure()->once(std::move(instance)); } /** @@ -20121,7 +44153,7 @@ public: */ template void erase(connection conn) { - assure().erase(std::move(conn)); + assure()->erase(std::move(conn)); } /** @@ -20134,7 +44166,7 @@ public: */ template void clear() { - assure().clear(); + assure()->clear(); } /** @@ -20158,7 +44190,8 @@ public: */ template [[nodiscard]] bool empty() const { - return assure().empty(); + const auto *cpool = assure(); + return !cpool || cpool->empty(); } /** @@ -20172,7 +44205,7 @@ public: } private: - mutable std::vector> pools{}; + std::vector> pools{}; }; @@ -20182,4 +44215,527 @@ private: #endif // #include "signal/sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + + +#include +#include +#include +#include +#include +#include +// #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 Function A valid function type. + */ +template +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 Function A valid function type. + */ +template +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. + */ +template +class sigh { + /*! @brief A sink is allowed to modify a signal. */ + friend class sink; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Sink type. */ + using sink_type = sink; + + /** + * @brief Instance type when it comes to connecting member functions. + * @tparam Class Type of class to which the member function belongs. + */ + template + 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 + void collect(Func func, Args... args) const { + for(auto &&call: calls) { + if constexpr(std::is_void_v) { + if constexpr(std::is_invocable_r_v) { + call(args...); + if(func()) { break; } + } else { + call(args...); + func(); + } + } else { + if constexpr(std::is_invocable_r_v) { + if(func(call(args...))) { break; } + } else { + func(call(args...)); + } + } + } + } + +private: + std::vector> 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 + friend class sink; + + connection(delegate 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(disconnect); + } + + /*! @brief Breaks the connection. */ + void release() { + if(disconnect) { + disconnect(signal); + disconnect.reset(); + } + } + +private: + delegate 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.
+ * 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 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 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(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.
+ * 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. + */ +template +class sink { + using signal_type = sigh; + using difference_type = typename std::iterator_traits::difference_type; + + template + static void release(Type value_or_instance, void *signal) { + sink{*static_cast(signal)}.disconnect(value_or_instance); + } + + template + static void release(void *signal) { + sink{*static_cast(signal)}.disconnect(); + } + +public: + /** + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. + */ + sink(sigh &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 + [[nodiscard]] sink before() { + delegate call{}; + call.template connect(); + + const auto &calls = signal->calls; + const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + + sink other{*this}; + other.offset = std::distance(it, calls.cend()); + 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 + [[nodiscard]] sink before(Type &&value_or_instance) { + delegate call{}; + call.template connect(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 = std::distance(it, calls.cend()); + 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 + [[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 + [[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.instance() == value_or_instance; + }); + + other.offset = std::distance(it, calls.cend()); + } + + 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 + connection connect() { + disconnect(); + + delegate call{}; + call.template connect(); + signal->calls.insert(signal->calls.end() - offset, std::move(call)); + + delegate conn{}; + conn.template connect<&release>(); + 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.
+ * 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 + connection connect(Type &&value_or_instance) { + disconnect(value_or_instance); + + delegate call{}; + call.template connect(value_or_instance); + signal->calls.insert(signal->calls.end() - offset, std::move(call)); + + delegate conn{}; + conn.template connect<&release>(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 + void disconnect() { + auto &calls = signal->calls; + delegate call{}; + call.template connect(); + 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 + void disconnect(Type &&value_or_instance) { + auto &calls = signal->calls; + delegate call{}; + call.template connect(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 + 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 + void disconnect(Type *value_or_instance) { + if(value_or_instance) { + auto &calls = signal->calls; + calls.erase(std::remove_if(calls.begin(), calls.end(), [value_or_instance](const auto &delegate) { + return delegate.instance() == value_or_instance; + }), 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 function 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. + */ +template +sink(sigh &) +-> sink; + + +} + + +#endif